diff --git a/.github/workflows/blocked.yml b/.github/workflows/blocked.yml deleted file mode 100644 index d6e592cb0d..0000000000 --- a/.github/workflows/blocked.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Prevent blocked -on: - pull_request: - types: [opened, labeled, unlabeled] -jobs: - prevent-blocked: - name: Prevent blocked - runs-on: ubuntu-latest - permissions: - pull-requests: read - steps: - - name: Add notice - uses: actions/github-script@v7 - if: contains(github.event.pull_request.labels.*.name, 'X-Blocked') - with: - script: | - core.setFailed("PR has been labeled with X-Blocked; it cannot be merged."); diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ce7acf880..20b212ca38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: retention-days: 5 overwrite: true if-no-files-found: error - - uses: rnkdsh/action-upload-diawi@v1.5.6 + - uses: rnkdsh/action-upload-diawi@v1.5.7 id: diawi # Do not fail the whole build if Diawi upload fails continue-on-error: true @@ -89,7 +89,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Compile release sources if: ${{ matrix.variant == 'release' }} - run: ./gradlew compileReleaseSources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES + run: ./gradlew bundleGplayRelease -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Compile nightly sources if: ${{ matrix.variant == 'nightly' }} run: ./gradlew compileGplayNightlySources -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 9182a9af3c..148eb6a670 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -33,7 +33,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 5bfc0b6982..2d39bfa7df 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules diff --git a/.github/workflows/nightly_enterprise.yml b/.github/workflows/nightly_enterprise.yml index c7700ed56b..797879c8c1 100644 --- a/.github/workflows/nightly_enterprise.yml +++ b/.github/workflows/nightly_enterprise.yml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} - name: Clone submodules diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000000..72a4dfe0a7 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,77 @@ +name: Pull Request +on: + pull_request: + types: [ opened, edited, labeled, unlabeled, synchronize ] + workflow_call: + secrets: + ELEMENT_BOT_TOKEN: + required: true + +jobs: + prevent-blocked: + name: Prevent blocked + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - name: Add notice + uses: actions/github-script@v7 + if: contains(github.event.pull_request.labels.*.name, 'X-Blocked') + with: + script: | + core.setFailed("PR has been labeled with X-Blocked; it cannot be merged."); + + community-prs: + name: Label Community PRs + runs-on: ubuntu-latest + if: github.event.action == 'opened' + permissions: + pull-requests: write + steps: + - name: Check membership + if: github.event.pull_request.user.login != 'renovate[bot]' + uses: tspascoal/get-user-teams-membership@57e9f42acd78f4d0f496b3be4368fc5f62696662 # v3 + id: teams + with: + username: ${{ github.event.pull_request.user.login }} + organization: element-hq + team: Vector Core + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }} + - name: Add label + if: steps.teams.outputs.isTeamMember == 'false' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['Z-Community-PR'] + }); + + close-if-fork-develop: + name: Forbid develop branch fork contributions + runs-on: ubuntu-latest + if: > + github.event.action == 'opened' && + github.event.pull_request.head.ref == 'develop' && + github.event.pull_request.head.repo.full_name != github.repository + steps: + - name: Close pull request + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: "Thanks for opening this pull request, unfortunately we do not accept contributions from the main" + + " branch of your fork, please re-open once you switch to an alternative branch for everyone's sanity.", + }); + + github.rest.pulls.update({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + state: 'closed' + }); diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 77482ec94a..505eb2607c 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -19,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} @@ -77,7 +77,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} @@ -117,7 +117,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} @@ -161,7 +161,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} @@ -201,7 +201,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} @@ -241,7 +241,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9de267d3f..c9656970db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ef7a8843c2..b5b4359970 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Add SSH private keys for submodule repositories - uses: webfactory/ssh-agent@v0.9.0 + uses: webfactory/ssh-agent@v0.9.1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} with: ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }} diff --git a/.gitignore b/.gitignore index 12bdb4eda8..d4bdf725fc 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ captures/ .idea/.name .idea/androidTestResultsUserPreferences.xml .idea/assetWizardSettings.xml +.idea/AndroidProjectSystem.xml .idea/compiler.xml .idea/deploymentTargetDropDown.xml .idea/deploymentTargetSelector.xml diff --git a/CHANGES.md b/CHANGES.md index 73fb9cb551..296e714fec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,43 @@ +Changes in Element X v25.03.1 +============================= + + + +## What's Changed +### ✨ Features +* Enable the Event cache by default. by @bmarty in https://github.com/element-hq/element-x-android/pull/4373 +### 🙌 Improvements +* change(create room) : use history visibility "invited" by @ganfra in https://github.com/element-hq/element-x-android/pull/4335 +* change(room directory) : move the the room directory entry by @ganfra in https://github.com/element-hq/element-x-android/pull/4348 +* [Change] Invited state room preview by @ganfra in https://github.com/element-hq/element-x-android/pull/4353 +* change(left room snackbar) : manage cancel knock and decline invite by @ganfra in https://github.com/element-hq/element-x-android/pull/4360 +### 🐛 Bugfixes +* Restore manual `Client` cleanup on session logout by @jmartinesp in https://github.com/element-hq/element-x-android/pull/4333 +### 🗣 Translations +* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/4346 +### 🧱 Build +* Fix typo on job name. by @bmarty in https://github.com/element-hq/element-x-android/pull/4352 +### Dependency upgrades +* chore(deps): update plugin ktlint to v12.2.0 by @renovate in https://github.com/element-hq/element-x-android/pull/4338 +* fix(deps): update dependency org.maplibre.gl:android-sdk to v11.8.2 by @renovate in https://github.com/element-hq/element-x-android/pull/4340 +* fix(deps): update dependency io.mockk:mockk to v1.13.17 by @renovate in https://github.com/element-hq/element-x-android/pull/4334 +* fix(deps): update kotlin to v2.1.10-1.0.31 by @renovate in https://github.com/element-hq/element-x-android/pull/4337 +* fix(deps): update dependency com.google.firebase:firebase-bom to v33.10.0 by @renovate in https://github.com/element-hq/element-x-android/pull/4339 +* Migrate to coil3 by @bmarty in https://github.com/element-hq/element-x-android/pull/4347 +* fix(deps): update dependency org.jsoup:jsoup to v1.19.1 by @renovate in https://github.com/element-hq/element-x-android/pull/4351 +* deps(rust sdk) : update to 25.03.05 by @ganfra in https://github.com/element-hq/element-x-android/pull/4370 +* Update dependency org.matrix.rustcomponents:sdk-android to v25.3.6 by @renovate in https://github.com/element-hq/element-x-android/pull/4371 +### Others +* Prevent PRs with the X-Blocked label from being merged by @robintown in https://github.com/element-hq/element-x-android/pull/4350 +* Fix some icon colors by @bmarty in https://github.com/element-hq/element-x-android/pull/4365 +* Remove PreferenceText, replace by ListItem. by @bmarty in https://github.com/element-hq/element-x-android/pull/4369 +* Show error screens in group calls by @robintown in https://github.com/element-hq/element-x-android/pull/4297 + +## New Contributors +* @robintown made their first contribution in https://github.com/element-hq/element-x-android/pull/4350 + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.03.0...v25.03.1 + Changes in Element X v25.03.0 ============================= diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 89181cf9cf..3b53b00cf1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,6 +10,7 @@ import com.android.build.api.variant.FilterConfiguration.FilterType.ABI import com.android.build.gradle.internal.tasks.factory.dependsOn import com.android.build.gradle.tasks.GenerateBuildConfig +import com.google.firebase.appdistribution.gradle.firebaseAppDistribution import config.BuildTimeConfig import extension.AssetCopyTask import extension.ComponentMergingStrategy diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt index 4f4e360f4f..a4bfe0c60d 100644 --- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt @@ -15,7 +15,7 @@ import io.element.android.x.di.AppComponent import io.element.android.x.di.DaggerAppComponent import io.element.android.x.info.logApplicationInfo import io.element.android.x.initializer.CrashInitializer -import io.element.android.x.initializer.TracingInitializer +import io.element.android.x.initializer.PlatformInitializer class ElementXApplication : Application(), DaggerComponentOwner { override val daggerComponent: AppComponent = DaggerAppComponent.factory().create(this) @@ -24,7 +24,7 @@ class ElementXApplication : Application(), DaggerComponentOwner { super.onCreate() AppInitializer.getInstance(this).apply { initializeComponent(CrashInitializer::class.java) - initializeComponent(TracingInitializer::class.java) + initializeComponent(PlatformInitializer::class.java) initializeComponent(CacheCleanerInitializer::class.java) } logApplicationInfo(this) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 2bdf083683..f80db878a7 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -64,6 +64,7 @@ class MainActivity : NodeActivity() { ElementThemeApp( appPreferencesStore = appBindings.preferencesStore(), enterpriseService = appBindings.enterpriseService(), + buildMeta = appBindings.buildMeta() ) { CompositionLocalProvider( LocalSnackbarDispatcher provides appBindings.snackbarDispatcher(), diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt index 4aa5d2c267..8525f6356b 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt @@ -13,8 +13,11 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.rageshake.api.reporter.BugReporter +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.di.AppScope +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.matrix.api.platform.InitPlatformService import io.element.android.libraries.matrix.api.tracing.TracingService import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.services.analytics.api.AnalyticsService @@ -25,6 +28,8 @@ interface AppBindings { fun tracingService(): TracingService + fun platformService(): InitPlatformService + fun bugReporter(): BugReporter fun lockScreenService(): LockScreenService @@ -38,4 +43,8 @@ interface AppBindings { fun analyticsService(): AnalyticsService fun enterpriseService(): EnterpriseService + + fun featureFlagService(): FeatureFlagService + + fun buildMeta(): BuildMeta } diff --git a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt b/app/src/main/kotlin/io/element/android/x/initializer/PlatformInitializer.kt similarity index 83% rename from app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt rename to app/src/main/kotlin/io/element/android/x/initializer/PlatformInitializer.kt index 5c53968ece..650480a0ee 100644 --- a/app/src/main/kotlin/io/element/android/x/initializer/TracingInitializer.kt +++ b/app/src/main/kotlin/io/element/android/x/initializer/PlatformInitializer.kt @@ -12,9 +12,9 @@ import android.system.Os import androidx.startup.Initializer import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration -import io.element.android.x.BuildConfig import io.element.android.x.di.AppBindings import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking @@ -22,22 +22,24 @@ import timber.log.Timber private const val ELEMENT_X_TARGET = "elementx" -class TracingInitializer : Initializer { +class PlatformInitializer : Initializer { override fun create(context: Context) { val appBindings = context.bindings() val tracingService = appBindings.tracingService() + val platformService = appBindings.platformService() val bugReporter = appBindings.bugReporter() Timber.plant(tracingService.createTimberTree(ELEMENT_X_TARGET)) val preferencesStore = appBindings.preferencesStore() + val featureFlagService = appBindings.featureFlagService() val logLevel = runBlocking { preferencesStore.getTracingLogLevelFlow().first() } val tracingConfiguration = TracingConfiguration( - writesToLogcat = BuildConfig.DEBUG, + writesToLogcat = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.PrintLogsToLogcat) }, writesToFilesConfiguration = defaultWriteToDiskConfiguration(bugReporter), logLevel = logLevel, extraTargets = listOf(ELEMENT_X_TARGET), ) bugReporter.setCurrentTracingLogLevel(logLevel.name) - tracingService.setupTracing(tracingConfiguration) + platformService.init(tracingConfiguration) // Also set env variable for rust back trace Os.setenv("RUST_BACKTRACE", "1", true) } diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index 8fbfba96a1..3dd40c5c1b 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -9,6 +9,7 @@ + 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 6526248ef8..dbf7ab6c6a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -73,17 +73,24 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.permalink.PermalinkData -import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withTimeout import kotlinx.parcelize.Parcelize import timber.log.Timber +import java.time.Duration +import java.time.Instant import java.util.Optional import java.util.UUID +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds +import kotlin.time.toKotlinDuration @ContributesNode(SessionScope::class) class LoggedInFlowNode @AssistedInject constructor( @@ -127,8 +134,35 @@ class LoggedInFlowNode @AssistedInject constructor( ) private val verificationListener = object : SessionVerificationServiceListener { - override fun onIncomingSessionRequest(sessionVerificationRequestDetails: SessionVerificationRequestDetails) { - backstack.singleTop(NavTarget.IncomingVerificationRequest(sessionVerificationRequestDetails)) + override fun onIncomingSessionRequest(verificationRequest: VerificationRequest.Incoming) { + // Without this launch the rendering and actual state of this Appyx node's children gets out of sync, resulting in a crash. + // This might be because this method is called back from Rust in a background thread. + lifecycleScope.launch { + val receivedAt = Instant.now() + + // Wait until the app is in foreground to display the incoming verification request + appNavigationStateService.appNavigationState.first { it.isInForeground } + + // TODO there should also be a timeout for > 10 minutes elapsed since the request was created, but the SDK doesn't expose that info yet + val now = Instant.now() + val elapsedTimeSinceReceived = Duration.between(receivedAt, now).toKotlinDuration() + + // Discard the incoming verification request if it has timed out + if (elapsedTimeSinceReceived > 2.minutes) { + Timber.w("Incoming verification request ${verificationRequest.details.flowId} discarded due to timeout.") + return@launch + } + + // Wait for the RoomList UI to be ready so the incoming verification screen can be displayed on top of it + // Otherwise, the RoomList UI may be incorrectly displayed on top + withTimeout(5.seconds) { + backstack.elements.first { elements -> + elements.any { it.key.navTarget == NavTarget.RoomList } + } + } + + backstack.singleTop(NavTarget.IncomingVerificationRequest(verificationRequest)) + } } } @@ -218,7 +252,7 @@ class LoggedInFlowNode @AssistedInject constructor( data object LogoutForNativeSlidingSyncMigrationNeeded : NavTarget @Parcelize - data class IncomingVerificationRequest(val data: SessionVerificationRequestDetails) : NavTarget + data class IncomingVerificationRequest(val data: VerificationRequest.Incoming) : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { diff --git a/appnav/src/main/res/values-es/translations.xml b/appnav/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..20f155513e --- /dev/null +++ b/appnav/src/main/res/values-es/translations.xml @@ -0,0 +1,6 @@ + + + "Cerrar sesión y actualizar" + "%1$s ya no es compatible con el antiguo protocolo. Cierra sesión y vuelve a iniciarla para seguir usando la aplicación." + "Tu servidor base ya no es compatible con el protocolo anterior. Cierra sesión y vuelve a iniciarla para seguir usando la aplicación." + diff --git a/appnav/src/main/res/values-eu/translations.xml b/appnav/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..11bef5da06 --- /dev/null +++ b/appnav/src/main/res/values-eu/translations.xml @@ -0,0 +1,4 @@ + + + "Zure zerbitzaria ez da bateragarria protokolo zaharrarekin. Amaitu saioa eta hasi berriro aplikazioa erabiltzen jarraitzeko." + diff --git a/appnav/src/main/res/values-fi/translations.xml b/appnav/src/main/res/values-fi/translations.xml index bdd10a92c4..14605ea6e1 100644 --- a/appnav/src/main/res/values-fi/translations.xml +++ b/appnav/src/main/res/values-fi/translations.xml @@ -1,5 +1,6 @@ "Kirjaudu Ulos & Päivitä" + "%1$s ei enää tue vanhaa protokollaa. Kirjaudu ulos ja takaisin sisään jatkaaksesi sovelluksen käyttöä." "Kotipalvelimesi ei enää tue vanhaa protokollaa. Kirjaudu ulos ja takaisin sisään jatkaaksesi sovelluksen käyttöä." diff --git a/appnav/src/main/res/values-hu/translations.xml b/appnav/src/main/res/values-hu/translations.xml index e4b24f811b..a4dca1c214 100644 --- a/appnav/src/main/res/values-hu/translations.xml +++ b/appnav/src/main/res/values-hu/translations.xml @@ -1,5 +1,6 @@ "Kijelentkezés és frissítés" + "%1$s már nem támogatja a régi protokollt. Kérjük, jelentkezzen ki és jelentkezzen be újra az alkalmazás használatának folytatásához." "A Matrix-kiszolgáló már nem támogatja a régi protokollt. Az alkalmazás további használatához jelentkezzen ki és be." diff --git a/appnav/src/main/res/values-it/translations.xml b/appnav/src/main/res/values-it/translations.xml index d93dc1e688..725604ed3f 100644 --- a/appnav/src/main/res/values-it/translations.xml +++ b/appnav/src/main/res/values-it/translations.xml @@ -1,5 +1,6 @@ "Esci e aggiorna" + "%1$s non supporta più il vecchio protocollo. Esci e accedi nuovamente per continuare a utilizzare l\'app." "Il tuo homeserver non supporta più il vecchio protocollo. Esci e rientra per continuare a usare l\'app." diff --git a/appnav/src/main/res/values-nb/translations.xml b/appnav/src/main/res/values-nb/translations.xml new file mode 100644 index 0000000000..cf42633154 --- /dev/null +++ b/appnav/src/main/res/values-nb/translations.xml @@ -0,0 +1,5 @@ + + + "Logg ut og oppgrader" + "Hjemmeserveren din støtter ikke lenger den gamle protokollen. Vennligst logg ut og inn igjen for å fortsette å bruke appen." + diff --git a/appnav/src/main/res/values-pl/translations.xml b/appnav/src/main/res/values-pl/translations.xml index 0df56520b1..68de66a54e 100644 --- a/appnav/src/main/res/values-pl/translations.xml +++ b/appnav/src/main/res/values-pl/translations.xml @@ -1,5 +1,6 @@ "Wyloguj się i zaktualizuj" + "%1$s już nie wspiera starego protokołu. Zaloguj się ponownie, aby dalej korzystać z aplikacji." "Twój serwer domowy już nie wspiera starego protokołu. Zaloguj się ponownie, aby kontynuować korzystanie z aplikacji." diff --git a/appnav/src/main/res/values-pt/translations.xml b/appnav/src/main/res/values-pt/translations.xml index 8b29a17ecf..d606a8d103 100644 --- a/appnav/src/main/res/values-pt/translations.xml +++ b/appnav/src/main/res/values-pt/translations.xml @@ -1,5 +1,6 @@ "Sair & Atualizar" + "%1$s já não suporta o protocolo antigo. Termina a sessão e volta a iniciar sessão para continuares a utilizar a aplicação." "Seu homeserver não suporta mais o protocolo antigo. Termine sessão e volte a iniciar sessão para continuar a utilizar a aplicação." diff --git a/appnav/src/main/res/values-sv/translations.xml b/appnav/src/main/res/values-sv/translations.xml index 89726dd893..408e02057e 100644 --- a/appnav/src/main/res/values-sv/translations.xml +++ b/appnav/src/main/res/values-sv/translations.xml @@ -1,5 +1,6 @@ "Logga ut och uppgradera" + "%1$s stöder inte längre det gamla protokollet. Logga ut och logga in igen för att fortsätta använda appen." "Din hemserver stöder inte längre det gamla protokollet. Logga ut och logga in igen för att fortsätta använda appen." diff --git a/appnav/src/main/res/values-uk/translations.xml b/appnav/src/main/res/values-uk/translations.xml index d2ee822d30..b65ab1a1cb 100644 --- a/appnav/src/main/res/values-uk/translations.xml +++ b/appnav/src/main/res/values-uk/translations.xml @@ -1,5 +1,6 @@ "Вийти та оновити" + "%1$s більше не підтримує старий протокол. Вийдіть і знов увійдіть, щоб продовжити користуватися застосунком." "Ваш домашній сервер більше не підтримує старий протокол. Будь ласка, вийдіть і увійдіть знову, щоб продовжити використання програми." diff --git a/appnav/src/main/res/values-zh-rTW/translations.xml b/appnav/src/main/res/values-zh-rTW/translations.xml index 5ff8996b04..ea69a33df8 100644 --- a/appnav/src/main/res/values-zh-rTW/translations.xml +++ b/appnav/src/main/res/values-zh-rTW/translations.xml @@ -1,5 +1,6 @@ "登出並升級" + "%1$s 不再支援舊版通訊協定。請登出並重新登入以繼續使用應用程式。" "您的家伺服器不再支援舊協定。請登出並重新登入以繼續使用應用程式。" diff --git a/enterprise b/enterprise index 6d96bf58ae..665a15a190 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 6d96bf58aec2ecc77b408858272cd64ec26e10d0 +Subproject commit 665a15a1907a116816ffd1653bccfeaeeb7a2968 diff --git a/fastlane/metadata/android/en-US/changelogs/202503020.txt b/fastlane/metadata/android/en-US/changelogs/202503020.txt new file mode 100644 index 0000000000..8955ade680 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202503020.txt @@ -0,0 +1,2 @@ +Main changes in this version: bug fixes and improvements. +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/features/analytics/api/src/main/res/values-eu/translations.xml b/features/analytics/api/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..872363f92f --- /dev/null +++ b/features/analytics/api/src/main/res/values-eu/translations.xml @@ -0,0 +1,7 @@ + + + "Partekatu erabilerari buruzko datu anonimoak arazoak identifikatzen laguntzeko." + "Gure baldintza guztiak irakur ditzakezu %1$s ." + "hemen" + "Partekatu analisi-datuak" + diff --git a/features/analytics/impl/src/main/res/values-eu/translations.xml b/features/analytics/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..2b3a29c0af --- /dev/null +++ b/features/analytics/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,9 @@ + + + "Partekatu erabilerari buruzko datu anonimoak arazoak identifikatzen laguntzeko." + "Gure baldintza guztiak irakur ditzakezu %1$s ." + "hemen" + "Edozein unetan desaktibatu dezakezu" + "Ez ditugu zure datuak hirugarrenekin partekatuko" + "Lagundu %1$s hobetzen" + diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt index 8345ced619..de31b0b02b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt @@ -110,7 +110,7 @@ class RingingCallNotificationCreator @Inject constructor( ) return NotificationCompat.Builder(context, notificationChannelId) - .setSmallIcon(CommonDrawables.ic_notification_small) + .setSmallIcon(CommonDrawables.ic_notification) .setPriority(NotificationCompat.PRIORITY_MAX) .setCategory(NotificationCompat.CATEGORY_CALL) .setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, declineIntent, answerIntent).setIsVideo(true)) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/services/CallForegroundService.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/services/CallForegroundService.kt index dc4541b277..42c2e19b56 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/services/CallForegroundService.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/services/CallForegroundService.kt @@ -67,7 +67,7 @@ class CallForegroundService : Service() { val callActivityIntent = Intent(this, ElementCallActivity::class.java) val pendingIntent = PendingIntentCompat.getActivity(this, 0, callActivityIntent, 0, false) val notification = NotificationCompat.Builder(this, foregroundServiceChannel.id) - .setSmallIcon(IconCompat.createWithResource(this, CommonDrawables.ic_notification_small)) + .setSmallIcon(IconCompat.createWithResource(this, CommonDrawables.ic_notification)) .setContentTitle(getString(R.string.call_foreground_service_title_android)) .setContentText(getString(R.string.call_foreground_service_message_android)) .setContentIntent(pendingIntent) diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt index af7323eea4..a1906ff99a 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/ElementCallActivity.kt @@ -48,6 +48,7 @@ import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore import timber.log.Timber @@ -64,6 +65,7 @@ class ElementCallActivity : @Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var enterpriseService: EnterpriseService @Inject lateinit var pictureInPicturePresenter: PictureInPicturePresenter + @Inject lateinit var buildMeta: BuildMeta private lateinit var presenter: Presenter @@ -114,6 +116,7 @@ class ElementCallActivity : ElementThemeApp( appPreferencesStore = appPreferencesStore, enterpriseService = enterpriseService, + buildMeta = buildMeta, ) { val state = presenter.present() eventSink = state.eventSink diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt index 445e3c4f9a..9669285ce0 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/IncomingCallActivity.kt @@ -21,6 +21,7 @@ import io.element.android.features.call.impl.utils.ActiveCallManager import io.element.android.features.call.impl.utils.CallState import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.coroutines.flow.filter @@ -51,6 +52,9 @@ class IncomingCallActivity : AppCompatActivity() { @Inject lateinit var enterpriseService: EnterpriseService + @Inject + lateinit var buildMeta: BuildMeta + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -71,6 +75,7 @@ class IncomingCallActivity : AppCompatActivity() { ElementThemeApp( appPreferencesStore = appPreferencesStore, enterpriseService = enterpriseService, + buildMeta = buildMeta, ) { IncomingCallScreen( notificationData = notificationData, diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt index 097e8a6531..8d34cfcfd4 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt @@ -38,7 +38,8 @@ class DefaultCallWidgetProvider @Inject constructor( val baseUrl = appPreferencesStore.getCustomElementCallBaseUrlFlow().firstOrNull() ?: elementCallBaseUrlProvider.provides(matrixClient) ?: ElementCallConfig.DEFAULT_BASE_URL - val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = room.isEncrypted) + val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow() + val widgetSettings = callWidgetSettingsProvider.provide(baseUrl, encrypted = isEncrypted) val callUrl = room.generateWidgetWebViewUrl( widgetSettings = widgetSettings, clientId = clientId, diff --git a/features/call/impl/src/main/res/values-es/translations.xml b/features/call/impl/src/main/res/values-es/translations.xml index 2e93185926..b0c3092efa 100644 --- a/features/call/impl/src/main/res/values-es/translations.xml +++ b/features/call/impl/src/main/res/values-es/translations.xml @@ -3,4 +3,5 @@ "Llamada en curso" "Pulsa para regresar a la llamada" "☎️ Llamada en curso" + "Llamada de Element Call entrante" diff --git a/features/call/impl/src/main/res/values-eu/translations.xml b/features/call/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..9985c5633e --- /dev/null +++ b/features/call/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,7 @@ + + + "Abian den deia" + "Sakatu deira itzultzeko." + "☎️ Deia abian" + "Element deia jasotzen" + diff --git a/features/createroom/impl/src/main/res/values-be/translations.xml b/features/createroom/impl/src/main/res/values-be/translations.xml index 01c0fea1bd..4706fc4092 100644 --- a/features/createroom/impl/src/main/res/values-be/translations.xml +++ b/features/createroom/impl/src/main/res/values-be/translations.xml @@ -13,5 +13,6 @@ "Назва пакоя" "Стварыце пакой" "Тэма (неабавязкова)" + "Каталог пакояў" "Пры спробе пачаць чат адбылася памылка" diff --git a/features/createroom/impl/src/main/res/values-cs/translations.xml b/features/createroom/impl/src/main/res/values-cs/translations.xml index d26130ce0f..53592e0db0 100644 --- a/features/createroom/impl/src/main/res/values-cs/translations.xml +++ b/features/createroom/impl/src/main/res/values-cs/translations.xml @@ -19,6 +19,7 @@ To můžete kdykoli změnit v nastavení místnosti." "Viditelnost místnosti" "Vytvořit místnost" "Téma (nepovinné)" + "Adresář místností" "Při pokusu o zahájení chatu došlo k chybě" "Vstoupit do místnosti pomocí adresy" "Neplatná adresa" diff --git a/features/createroom/impl/src/main/res/values-de/translations.xml b/features/createroom/impl/src/main/res/values-de/translations.xml index aec32766a2..9ca4d52012 100644 --- a/features/createroom/impl/src/main/res/values-de/translations.xml +++ b/features/createroom/impl/src/main/res/values-de/translations.xml @@ -19,5 +19,12 @@ Sie können dies aber jederzeit in den Chatroomeinstellungen ändern." " Sichtbarkeit des Chatrooms" "Raum erstellen" "Thema (optional)" + "Raum-Verzeichnis" "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten" + "Raum per Adresse betreten" + "Keine gültige Adresse" + "Eintreten…" + "Passender Raum gefunden" + "Raum nicht gefunden" + "z. B. #room -name:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-el/translations.xml b/features/createroom/impl/src/main/res/values-el/translations.xml index 4c414a6b5b..3ea4924c8d 100644 --- a/features/createroom/impl/src/main/res/values-el/translations.xml +++ b/features/createroom/impl/src/main/res/values-el/translations.xml @@ -18,5 +18,6 @@ "Ορατότητα δωματίου" "Δημιούργησε ένα δωμάτιο" "Θέμα (προαιρετικό)" + "Κατάλογος δωματίων" "Παρουσιάστηκε σφάλμα κατά την προσπάθεια έναρξης μιας συνομιλίας" diff --git a/features/createroom/impl/src/main/res/values-es/translations.xml b/features/createroom/impl/src/main/res/values-es/translations.xml index 2e36d3022b..4611c7a211 100644 --- a/features/createroom/impl/src/main/res/values-es/translations.xml +++ b/features/createroom/impl/src/main/res/values-es/translations.xml @@ -3,11 +3,24 @@ "Nueva sala" "Invitar personas" "Se ha producido un error al crear la sala" - "Los mensajes de esta sala están cifrados. La encriptación no se puede desactivar después." - "Sala privada (sólo con invitación)" - "Los mensajes no están cifrados y cualquiera puede leerlos. Puedes activar la encriptación más adelante." + "Solo las personas invitadas pueden acceder a esta sala. Todos los mensajes están cifrados de extremo a extremo." + "Sala privada" + "Cualquiera puede encontrar esta sala. +Puedes cambiar esto en cualquier momento en los ajustes de la sala." + "Sala pública" + "Cualquiera puede unirse a esta sala" + "Cualquiera" + "Acceso a la sala" + "Cualquiera puede solicitar unirse a la sala, pero un administrador o un moderador tendrá que aceptar la solicitud" + "Solicitud para unirse" + "Para que esta sala sea visible en el directorio de salas públicas, necesitarás una dirección de sala." + "Dirección de la sala" "Nombre de la sala" + "Visibilidad de la sala" "Crear una sala" "Tema (opcional)" + "Directorio de salas" "Se ha producido un error al intentar iniciar un chat" + "Introducir…" + "p. ej., #nombre-de-la-sala:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-et/translations.xml b/features/createroom/impl/src/main/res/values-et/translations.xml index 9600a02a22..57a26106b3 100644 --- a/features/createroom/impl/src/main/res/values-et/translations.xml +++ b/features/createroom/impl/src/main/res/values-et/translations.xml @@ -19,6 +19,7 @@ Sa võid seda jututoa seadistustest alati muuta." "Jututoa nähtavus" "Loo jututuba" "Teema (kui soovid lisada)" + "Jututubade kataloog" "Vestluse alustamisel tekkis viga" "Liitu jututoaga aadressi alusel" "See pole kehtiv aadress" diff --git a/features/createroom/impl/src/main/res/values-eu/translations.xml b/features/createroom/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..514c30e19d --- /dev/null +++ b/features/createroom/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,21 @@ + + + "Gela berria" + "Gonbidatu jendea" + "Errorea gertatu da gela sortzean" + "Gonbidatutako jendea soilik sar daiteke gelara. Mezu guztiak daude ertzetik ertzera zifratuta." + "Gela pribatua" + "Edonork aurki dezake gela hau. +Gelaren ezarpenetan aldatu dezakezu hobespena." + "Gela publikoa" + "Edonor sar daiteke gela honetara" + "Edonork" + "Gelarako sarbidea" + "Gelaren helbidea" + "Gelaren izena" + "Gelaren ikusgarritasuna" + "Sortu gela" + "Mintzagaia (aukerakoa)" + "Gelen direktorioa" + "Errorea gertatu da txata hasten saiatzean" + diff --git a/features/createroom/impl/src/main/res/values-fa/translations.xml b/features/createroom/impl/src/main/res/values-fa/translations.xml index d7f05a770c..0a52a6fc8d 100644 --- a/features/createroom/impl/src/main/res/values-fa/translations.xml +++ b/features/createroom/impl/src/main/res/values-fa/translations.xml @@ -10,4 +10,5 @@ "نام اتاق" "ایجاد اتاق" "موضوع (اختیاری)" + "فهرست اتاق‌ها" diff --git a/features/createroom/impl/src/main/res/values-fi/translations.xml b/features/createroom/impl/src/main/res/values-fi/translations.xml index 39b0d54646..cdb6d04d59 100644 --- a/features/createroom/impl/src/main/res/values-fi/translations.xml +++ b/features/createroom/impl/src/main/res/values-fi/translations.xml @@ -19,5 +19,12 @@ Voit muuttaa tämän milloin tahansa huoneen asetuksista." "Huoneen näkyvyys" "Luo huone" "Aihe (valinnainen)" + "Huoneluettelo" "Keskustelun aloituksessa tapahtui virhe" + "Liity huoneeseen osoitteella" + "Osoite ei ole kelvollinen" + "Syötä…" + "Täsmäävä huone löytyi" + "Huonetta ei löytynyt" + "esim. #huoneen-nimi:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml index c41a1c60d4..3f9e01dc85 100644 --- a/features/createroom/impl/src/main/res/values-fr/translations.xml +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -19,6 +19,7 @@ Vous pouvez modifier cela à tout moment dans les paramètres du salon.""Visibilité du salon" "Créer un salon" "Sujet (facultatif)" + "Annuaire des salons" "Une erreur s’est produite lors de la tentative de création de la discussion" "Saisir une adresse de salon" "Ce n’est pas une adresse valide" diff --git a/features/createroom/impl/src/main/res/values-hu/translations.xml b/features/createroom/impl/src/main/res/values-hu/translations.xml index 244d936eef..1e4a721279 100644 --- a/features/createroom/impl/src/main/res/values-hu/translations.xml +++ b/features/createroom/impl/src/main/res/values-hu/translations.xml @@ -19,5 +19,12 @@ Ezt bármikor módosíthatja a szobabeállításokban." "Szoba láthatósága" "Szoba létrehozása" "Téma (nem kötelező)" + "Szobakatalógus" "Hiba történt a csevegés indításakor" + "Csatlakozás a szobához cím szerint" + "Nem érvényes cím" + "Írja be…" + "Megfelelő szoba található" + "Szoba nem található" + "pl. #szoba-neve:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-in/translations.xml b/features/createroom/impl/src/main/res/values-in/translations.xml index 32a5a0b25a..7fb8b685d2 100644 --- a/features/createroom/impl/src/main/res/values-in/translations.xml +++ b/features/createroom/impl/src/main/res/values-in/translations.xml @@ -18,5 +18,6 @@ Anda dapat mengubah ini kapan pun dalam pengaturan ruangan." "Keterlihatan ruangan" "Buat ruangan" "Topik (opsional)" + "Direktori ruangan" "Terjadi kesalahan saat mencoba memulai obrolan" diff --git a/features/createroom/impl/src/main/res/values-it/translations.xml b/features/createroom/impl/src/main/res/values-it/translations.xml index 9cc30fc0e6..701ebc967f 100644 --- a/features/createroom/impl/src/main/res/values-it/translations.xml +++ b/features/createroom/impl/src/main/res/values-it/translations.xml @@ -19,5 +19,12 @@ Puoi modificarlo in qualsiasi momento nelle impostazioni della stanza." "Visibilità della stanza" "Crea una stanza" "Argomento (facoltativo)" + "Elenco delle stanze" "Si è verificato un errore durante il tentativo di avviare una chat" + "Accedi alla stanza tramite indirizzo" + "Indirizzo non valido" + "Inserisci…" + "Stanza trovata" + "Stanza non trovata" + "ad esempio #room -name:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-ka/translations.xml b/features/createroom/impl/src/main/res/values-ka/translations.xml index 58b02cbe96..bb4a430320 100644 --- a/features/createroom/impl/src/main/res/values-ka/translations.xml +++ b/features/createroom/impl/src/main/res/values-ka/translations.xml @@ -10,5 +10,6 @@ "ოთახის სახელი" "ოთახის შექმნა" "თემა (სურვილისამებრ)" + "ოთახის კატალოგი" "ჩატის დაწყების მცდელობისას შეცდომა მოხდა" diff --git a/features/createroom/impl/src/main/res/values-nb/translations.xml b/features/createroom/impl/src/main/res/values-nb/translations.xml index 4692daad04..98ea87e622 100644 --- a/features/createroom/impl/src/main/res/values-nb/translations.xml +++ b/features/createroom/impl/src/main/res/values-nb/translations.xml @@ -8,8 +8,14 @@ "Alle kan finne dette rommet. Du kan endre dette når som helst i rominnstillingene." "Offentlig rom" + "Alle kan bli med i dette rommet" + "Alle kan be om å få bli med i rommet, men en administrator eller moderator må godta forespørselen" + "Be om å bli med" + "Romadresse" "Romnavn" + "Romsynlighet" "Opprett et rom" "Emne (valgfritt)" + "Romkatalog" "Det oppstod en feil når du prøvde å starte en chat" diff --git a/features/createroom/impl/src/main/res/values-nl/translations.xml b/features/createroom/impl/src/main/res/values-nl/translations.xml index c1f0fd92f5..7cdeffcfe6 100644 --- a/features/createroom/impl/src/main/res/values-nl/translations.xml +++ b/features/createroom/impl/src/main/res/values-nl/translations.xml @@ -16,5 +16,6 @@ Je kunt dit op elk gewenst moment wijzigen in de kamerinstellingen." "Naam van de kamer" "Creëer een kamer" "Onderwerp (optioneel)" + "Kamergids" "Er is een fout opgetreden bij het starten van een chat" diff --git a/features/createroom/impl/src/main/res/values-pl/translations.xml b/features/createroom/impl/src/main/res/values-pl/translations.xml index 7902fb4c3c..40d31e710d 100644 --- a/features/createroom/impl/src/main/res/values-pl/translations.xml +++ b/features/createroom/impl/src/main/res/values-pl/translations.xml @@ -19,5 +19,6 @@ Możesz to zmienić w ustawieniach pokoju." "Widoczność pomieszczenia" "Utwórz pokój" "Temat (opcjonalnie)" + "Katalog pokoi" "Wystąpił błąd podczas próby rozpoczęcia czatu" diff --git a/features/createroom/impl/src/main/res/values-pt-rBR/translations.xml b/features/createroom/impl/src/main/res/values-pt-rBR/translations.xml index 9308007154..a570bfef9f 100644 --- a/features/createroom/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/createroom/impl/src/main/res/values-pt-rBR/translations.xml @@ -10,5 +10,6 @@ Você pode mudar isso a qualquer momento nas configurações da sala." "Nome da sala" "Criar uma sala" "Tópico (opcional)" + "Diretório de salas" "Ocorreu um erro ao tentar iniciar um chat" diff --git a/features/createroom/impl/src/main/res/values-pt/translations.xml b/features/createroom/impl/src/main/res/values-pt/translations.xml index fcff2929e0..5e5f5fc9c3 100644 --- a/features/createroom/impl/src/main/res/values-pt/translations.xml +++ b/features/createroom/impl/src/main/res/values-pt/translations.xml @@ -19,5 +19,12 @@ Pode alterar esta opção nas definições da sala." "Visibilidade da sala" "Criar uma sala" "Descrição (opcional)" + "Diretório de salas" "Ocorreu um erro ao tentar iniciar uma conversa" + "Entrar na sala pelo endereço" + "Não é um endereço válido" + "Entrar…" + "Sala correspondente encontrado" + "Sala não encontrada" + "por exemplo, #sala:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-ro/translations.xml b/features/createroom/impl/src/main/res/values-ro/translations.xml index 7257a07aff..8d9e74b530 100644 --- a/features/createroom/impl/src/main/res/values-ro/translations.xml +++ b/features/createroom/impl/src/main/res/values-ro/translations.xml @@ -17,5 +17,6 @@ Puteți modifica acest lucru oricând în setări." "Numele camerei" "Creați o cameră" "Subiect (opțional)" + "Director de camere" "A apărut o eroare la încercarea începerii conversației" diff --git a/features/createroom/impl/src/main/res/values-ru/translations.xml b/features/createroom/impl/src/main/res/values-ru/translations.xml index b21bcb68ac..136e5bb7c9 100644 --- a/features/createroom/impl/src/main/res/values-ru/translations.xml +++ b/features/createroom/impl/src/main/res/values-ru/translations.xml @@ -19,5 +19,6 @@ "Видимость комнаты" "Создать комнату" "Тема (необязательно)" + "Каталог комнат" "Произошла ошибка при запуске чата" diff --git a/features/createroom/impl/src/main/res/values-sk/translations.xml b/features/createroom/impl/src/main/res/values-sk/translations.xml index 6126415d92..eaf6c2f19c 100644 --- a/features/createroom/impl/src/main/res/values-sk/translations.xml +++ b/features/createroom/impl/src/main/res/values-sk/translations.xml @@ -19,6 +19,7 @@ Môžete to kedykoľvek zmeniť v nastaveniach miestnosti." "Viditeľnosť miestnosti" "Vytvoriť miestnosť" "Téma (voliteľné)" + "Adresár miestností" "Pri pokuse o spustenie konverzácie sa vyskytla chyba" "Pripojte sa do miestnosti podľa adresy" "Neplatná adresa" diff --git a/features/createroom/impl/src/main/res/values-sv/translations.xml b/features/createroom/impl/src/main/res/values-sv/translations.xml index 9bb2aba511..316c9cc32e 100644 --- a/features/createroom/impl/src/main/res/values-sv/translations.xml +++ b/features/createroom/impl/src/main/res/values-sv/translations.xml @@ -19,5 +19,12 @@ Du kan ändra detta när som helst i rumsinställningarna." "Rumssynlighet" "Skapa ett rum" "Ämne (valfritt)" + "Rumskatalog" "Ett fel uppstod när du försökte starta en chatt" + "Gå med i rum med adress" + "Inte en giltig adress" + "Ange …" + "Matchande rum hittades" + "Rummet hittades inte" + "t.ex. #rumsnamn:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-tr/translations.xml b/features/createroom/impl/src/main/res/values-tr/translations.xml index 6b5ce93e92..c8e3136517 100644 --- a/features/createroom/impl/src/main/res/values-tr/translations.xml +++ b/features/createroom/impl/src/main/res/values-tr/translations.xml @@ -19,5 +19,6 @@ Bunu istediğiniz zaman oda ayarlarından değiştirebilirsiniz." "Oda görünürlüğü" "Bir oda oluştur" "Konu (isteğe bağlı)" + "Oda dizini" "Sohbet başlatmaya çalışırken bir hata oluştu" diff --git a/features/createroom/impl/src/main/res/values-uk/translations.xml b/features/createroom/impl/src/main/res/values-uk/translations.xml index 7c73a67640..a43172fb8b 100644 --- a/features/createroom/impl/src/main/res/values-uk/translations.xml +++ b/features/createroom/impl/src/main/res/values-uk/translations.xml @@ -19,5 +19,12 @@ "Видимість кімнати" "Створити кімнату" "Тема (необов\'язково)" + "Каталог кімнат" "Під час спроби почати бесіду сталася помилка" + "Приєднатися до кімнати за адресою" + "Недійсна адреса" + "Введіть…" + "Знайдено відповідну кімнату" + "Кімната не знайдена" + "наприклад, #room-name:matrix.org" diff --git a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml index 399e5bf1c8..04e2d53ea2 100644 --- a/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/createroom/impl/src/main/res/values-zh-rTW/translations.xml @@ -19,5 +19,6 @@ "聊天室能見度" "建立聊天室" "主題(非必填)" + "聊天室目錄" "嘗試開始聊天時發生錯誤" diff --git a/features/createroom/impl/src/main/res/values-zh/translations.xml b/features/createroom/impl/src/main/res/values-zh/translations.xml index 24741fdc5b..24d7df7f35 100644 --- a/features/createroom/impl/src/main/res/values-zh/translations.xml +++ b/features/createroom/impl/src/main/res/values-zh/translations.xml @@ -19,5 +19,6 @@ "房间可见性" "创建聊天室" "主题(可选)" + "聊天室目录" "在开始聊天时发生了错误" diff --git a/features/deactivation/impl/src/main/res/values-es/translations.xml b/features/deactivation/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..cd0757ba3e --- /dev/null +++ b/features/deactivation/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,14 @@ + + + "Confirma que quieres desactivar tu cuenta. Esta acción no se puede deshacer." + "Borrar todos mis mensajes" + "Advertencia: Futuros usuarios pueden ver conversaciones incompletas." + "Desactivar tu cuenta es %1$s:" + "irreversible" + "%1$s tu cuenta (no podrás volver a iniciar sesión y tu ID no se podrá volver a utilizar)." + "Inhabilitará permanentemente" + "Te eliminará de todas las salas de chat." + "Eliminará la información de tu cuenta de nuestro servidor de identidad." + "Tus mensajes seguirán siendo visibles para los usuarios registrados, pero no estarán disponibles para los usuarios nuevos o no registrados si decides eliminarlos." + "Desactivar cuenta" + diff --git a/features/deactivation/impl/src/main/res/values-eu/translations.xml b/features/deactivation/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..577349ed3d --- /dev/null +++ b/features/deactivation/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,5 @@ + + + "Kendu zure burua txat gela guztietatik." + "Desaktibatu kontua" + diff --git a/features/deactivation/impl/src/main/res/values-nb/translations.xml b/features/deactivation/impl/src/main/res/values-nb/translations.xml index 082160d636..cdff7f886d 100644 --- a/features/deactivation/impl/src/main/res/values-nb/translations.xml +++ b/features/deactivation/impl/src/main/res/values-nb/translations.xml @@ -5,8 +5,10 @@ "Advarsel: Fremtidige brukere vil kunne se ufullstendige samtaler." "Deaktivering av kontoen din er %1$s , det vil:" "irreversibel" + "%1$s kontoen din (du kan ikke logge på igjen, og ID-en din kan ikke brukes på nytt)." "Deaktiver permanent" "Fjern deg fra alle chatterom." "Slett kontoinformasjonen din fra vår identitetsserver." + "Meldingene dine vil fortsatt være synlige for registrerte brukere, men vil ikke være tilgjengelige for nye eller uregistrerte brukere hvis du velger å slette dem." "Deaktiver kontoen" diff --git a/features/deactivation/impl/src/main/res/values-nl/translations.xml b/features/deactivation/impl/src/main/res/values-nl/translations.xml index 5f7cf78847..e03ffa5046 100644 --- a/features/deactivation/impl/src/main/res/values-nl/translations.xml +++ b/features/deactivation/impl/src/main/res/values-nl/translations.xml @@ -7,7 +7,7 @@ "onomkeerbaar" "Je account %1$s (je kunt niet opnieuw inloggen en je ID kan niet opnieuw worden gebruikt)" "permanent uitschakelen" - "Je verwijderen uit alle chatrooms." + "Je verwijderen uit alle chatkamers." "Je accountgegevens verwijderen van onze identiteitsserver." "Je berichten zijn nog steeds zichtbaar voor geregistreerde gebruikers, maar niet beschikbaar voor nieuwe of niet-geregistreerde gebruikers als je ervoor kiest ze te verwijderen." "Account sluiten" diff --git a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt index 4fddfe283c..9c1cb8b981 100644 --- a/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt +++ b/features/enterprise/api/src/main/kotlin/io/element/android/features/enterprise/api/EnterpriseService.kt @@ -17,4 +17,7 @@ interface EnterpriseService { fun semanticColorsLight(): SemanticColors fun semanticColorsDark(): SemanticColors + + fun firebasePushGateway(): String? + fun unifiedPushDefaultPushGateway(): String? } diff --git a/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt index 898f59969c..2d66bb3968 100644 --- a/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt +++ b/features/enterprise/impl/src/main/kotlin/io/element/android/features/enterprise/impl/DefaultEnterpriseService.kt @@ -27,4 +27,7 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService { override fun semanticColorsLight(): SemanticColors = compoundColorsLight override fun semanticColorsDark(): SemanticColors = compoundColorsDark + + override fun firebasePushGateway(): String? = null + override fun unifiedPushDefaultPushGateway(): String? = null } diff --git a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt index fbb826ec73..ae71bf6b76 100644 --- a/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt +++ b/features/enterprise/test/src/main/kotlin/io/element/android/features/enterprise/test/FakeEnterpriseService.kt @@ -19,6 +19,8 @@ class FakeEnterpriseService( private val defaultHomeserverResult: () -> String? = { A_FAKE_HOMESERVER }, private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() }, private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() }, + private val firebasePushGatewayResult: () -> String? = { lambdaError() }, + private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, ) : EnterpriseService { override suspend fun isEnterpriseUser(sessionId: SessionId): Boolean = simulateLongTask { isEnterpriseUserResult(sessionId) @@ -36,6 +38,14 @@ class FakeEnterpriseService( return semanticColorsDarkResult() } + override fun firebasePushGateway(): String? { + return firebasePushGatewayResult() + } + + override fun unifiedPushDefaultPushGateway(): String? { + return unifiedPushDefaultPushGatewayResult() + } + companion object { const val A_FAKE_HOMESERVER = "a_fake_homeserver" } diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index d9cd993043..652f989acb 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -1,4 +1,5 @@ import extension.setupAnvil +import org.gradle.kotlin.dsl.test /* * Copyright 2023, 2024 New Vector Ltd. @@ -14,6 +15,12 @@ plugins { android { namespace = "io.element.android.features.ftue.impl" + + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } setupAnvil() @@ -30,6 +37,7 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) implementation(projects.features.analytics.api) + implementation(projects.features.logout.api) implementation(projects.features.securebackup.api) implementation(projects.features.verifysession.api) implementation(projects.services.analytics.api) @@ -37,12 +45,16 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.permissions.noop) implementation(projects.services.toolbox.api) + implementation(projects.appconfig) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.test.robolectric) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) testImplementation(projects.libraries.matrix.test) testImplementation(projects.services.analytics.test) testImplementation(projects.services.analytics.noop) 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 c5987508fe..034701fec4 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 @@ -16,7 +16,6 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BaseBackPressHandlerStrategy import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack @@ -38,8 +37,6 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SessionScope import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn @@ -59,7 +56,6 @@ class FtueFlowNode @AssistedInject constructor( backstack = BackStack( initialElement = NavTarget.Placeholder, savedStateMap = buildContext.savedStateMap, - backPressHandler = NoOpBackstackHandlerStrategy(), ), buildContext = buildContext, plugins = plugins, @@ -104,7 +100,7 @@ class FtueFlowNode @AssistedInject constructor( NavTarget.Placeholder -> { createNode(buildContext) } - NavTarget.SessionVerification -> { + is NavTarget.SessionVerification -> { val callback = object : FtueSessionVerificationFlowNode.Callback { override fun onDone() { moveToNextStepIfNeeded() @@ -175,11 +171,3 @@ class FtueFlowNode @AssistedInject constructor( } } } - -private class NoOpBackstackHandlerStrategy : BaseBackPressHandlerStrategy() { - override val canHandleBackPressFlow: StateFlow = MutableStateFlow(true) - - override fun onBackPressed() { - // No-op - } -} diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/di/FtueModule.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/di/FtueModule.kt new file mode 100644 index 0000000000..4387b3dc00 --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/di/FtueModule.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.ftue.impl.di + +import com.squareup.anvil.annotations.ContributesTo +import dagger.Binds +import dagger.Module +import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModePresenter +import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.SessionScope + +@ContributesTo(SessionScope::class) +@Module +interface FtueModule { + @Binds + fun bindChooseSelfVerificationMethodPresenter(presenter: ChooseSelfVerificationModePresenter): Presenter +} 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 4ff7e393b4..eeffbdfe50 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 @@ -9,6 +9,7 @@ package io.element.android.features.ftue.impl.sessionverification import android.os.Parcelable import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext @@ -17,15 +18,21 @@ import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack 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 dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.appconfig.LearnMoreConfig +import io.element.android.features.ftue.impl.sessionverification.choosemode.ChooseSelfVerificationModeNode import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.verifysession.api.VerifySessionEntryPoint 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.utils.OpenUrlInTabView import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -37,7 +44,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( private val secureBackupEntryPoint: SecureBackupEntryPoint, ) : BaseFlowNode( backstack = BackStack( - initialElement = NavTarget.Root(showDeviceVerifiedScreen = false), + initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, @@ -45,7 +52,10 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( ) { sealed interface NavTarget : Parcelable { @Parcelize - data class Root(val showDeviceVerifiedScreen: Boolean) : NavTarget + data object Root : NavTarget + + @Parcelize + data object UseAnotherDevice : NavTarget @Parcelize data object EnterRecoveryKey : NavTarget @@ -62,7 +72,7 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( override fun onDone() { lifecycleScope.launch { // Move to the completed state view in the verification flow - backstack.newRoot(NavTarget.Root(showDeviceVerifiedScreen = true)) + backstack.newRoot(NavTarget.UseAnotherDevice) } } } @@ -70,19 +80,43 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { is NavTarget.Root -> { - verifySessionEntryPoint.nodeBuilder(this, buildContext) - .params(VerifySessionEntryPoint.Params(navTarget.showDeviceVerifiedScreen)) - .callback(object : VerifySessionEntryPoint.Callback { - override fun onEnterRecoveryKey() { - backstack.push(NavTarget.EnterRecoveryKey) - } + val callback = object : ChooseSelfVerificationModeNode.Callback { + override fun onUseAnotherDevice() { + backstack.push(NavTarget.UseAnotherDevice) + } + override fun onUseRecoveryKey() { + backstack.push(NavTarget.EnterRecoveryKey) + } + + override fun onResetKey() { + backstack.push(NavTarget.ResetIdentity) + } + + override fun onLearnMoreAboutEncryption() { + learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL + } + } + + createNode(buildContext, plugins = listOf(callback)) + } + is NavTarget.UseAnotherDevice -> { + verifySessionEntryPoint.nodeBuilder(this, buildContext) + .params(VerifySessionEntryPoint.Params( + showDeviceVerifiedScreen = true, + verificationRequest = VerificationRequest.Outgoing.CurrentSession, + )) + .callback(object : VerifySessionEntryPoint.Callback { override fun onDone() { plugins().forEach { it.onDone() } } - override fun onResetKey() { - backstack.push(NavTarget.ResetIdentity) + override fun onBack() { + backstack.pop() + } + + override fun onLearnMoreAboutEncryption() { + learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL } }) .build() @@ -106,8 +140,12 @@ class FtueSessionVerificationFlowNode @AssistedInject constructor( } } + private val learnMoreUrl = mutableStateOf(null) + @Composable override fun View(modifier: Modifier) { BackstackView() + + OpenUrlInTabView(learnMoreUrl) } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeEvent.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeEvent.kt new file mode 100644 index 0000000000..99e5950693 --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeEvent.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.ftue.impl.sessionverification.choosemode + +sealed interface ChooseSelfVerificationModeEvent { + data object SignOut : ChooseSelfVerificationModeEvent +} 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 new file mode 100644 index 0000000000..687dc4aefe --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.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.ftue.impl.sessionverification.choosemode + +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 com.bumble.appyx.core.plugin.plugins +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.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) +class ChooseSelfVerificationModeNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: Presenter, + private val directLogoutView: DirectLogoutView, +) : Node(buildContext, plugins = plugins) { + interface Callback : Plugin { + fun onUseAnotherDevice() + fun onUseRecoveryKey() + fun onResetKey() + fun onLearnMoreAboutEncryption() + } + + private val callback = plugins().first() + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + + ChooseSelfVerificationModeView( + state = state, + onUseAnotherDevice = callback::onUseAnotherDevice, + onUseRecoveryKey = callback::onUseRecoveryKey, + onResetKey = callback::onResetKey, + onLearnMore = callback::onLearnMoreAboutEncryption, + modifier = modifier, + ) + + directLogoutView.Render(state = state.directLogoutState) + } +} 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 new file mode 100644 index 0000000000..0aec3b4e1d --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModePresenter.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.ftue.impl.sessionverification.choosemode + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.matrix.api.encryption.RecoveryState +import javax.inject.Inject + +class ChooseSelfVerificationModePresenter @Inject constructor( + private val encryptionService: EncryptionService, + private val directLogoutPresenter: Presenter, +) : Presenter { + @Composable + override fun present(): ChooseSelfVerificationModeState { + val isLastDevice by encryptionService.isLastDevice.collectAsState() + val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() + val canEnterRecoveryKey by remember { derivedStateOf { recoveryState == RecoveryState.INCOMPLETE } } + + val directLogoutState = directLogoutPresenter.present() + + fun eventHandler(event: ChooseSelfVerificationModeEvent) { + when (event) { + ChooseSelfVerificationModeEvent.SignOut -> directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) + } + } + + return ChooseSelfVerificationModeState( + isLastDevice = isLastDevice, + 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 new file mode 100644 index 0000000000..21c37a4ae2 --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeState.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.ftue.impl.sessionverification.choosemode + +import io.element.android.features.logout.api.direct.DirectLogoutState + +data class ChooseSelfVerificationModeState( + val isLastDevice: 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 new file mode 100644 index 0000000000..574aa367c6 --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeStateProvider.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.ftue.impl.sessionverification.choosemode + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +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), + ) +} + +fun aChooseSelfVerificationModeState( + isLastDevice: Boolean = false, + canEnterRecoveryKey: Boolean = true, +) = ChooseSelfVerificationModeState( + isLastDevice = isLastDevice, + 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 new file mode 100644 index 0000000000..9839f3bf7c --- /dev/null +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeView.kt @@ -0,0 +1,127 @@ +/* + * 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.ftue.impl.sessionverification.choosemode + +import androidx.activity.compose.BackHandler +import androidx.activity.compose.LocalActivity +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.ftue.impl.R +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.components.PageTitle +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.OutlinedButton +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.ui.strings.CommonStrings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ChooseSelfVerificationModeView( + state: ChooseSelfVerificationModeState, + onUseAnotherDevice: () -> Unit, + onUseRecoveryKey: () -> Unit, + onResetKey: () -> Unit, + onLearnMore: () -> Unit, + modifier: Modifier = Modifier +) { + val activity = LocalActivity.current + BackHandler { + activity?.finish() + } + + HeaderFooterPage( + modifier = modifier, + topBar = { + TopAppBar( + title = {}, + actions = { + TextButton( + text = stringResource(CommonStrings.action_signout), + onClick = { state.eventSink(ChooseSelfVerificationModeEvent.SignOut) } + ) + } + ) + }, + header = { + PageTitle( + iconStyle = BigIcon.Style.Default(CompoundIcons.LockSolid()), + title = stringResource(id = R.string.screen_identity_confirmation_title), + subtitle = stringResource(id = R.string.screen_identity_confirmation_subtitle) + ) + }, + footer = { + ButtonColumnMolecule( + modifier = Modifier.padding(bottom = 16.dp) + ) { + if (state.isLastDevice.not()) { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_identity_use_another_device), + onClick = onUseAnotherDevice, + ) + } + if (state.canEnterRecoveryKey) { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_enter_recovery_key), + onClick = onUseRecoveryKey, + ) + } + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_identity_confirmation_cannot_confirm), + onClick = onResetKey, + ) + } + } + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + Text( + modifier = Modifier + .clickable(onClick = onLearnMore) + .padding(vertical = 4.dp, horizontal = 16.dp), + text = stringResource(CommonStrings.action_learn_more), + style = ElementTheme.typography.fontBodyLgMedium + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun ChooseSelfVerificationModeViewPreview( + @PreviewParameter(ChooseSelfVerificationModeStateProvider::class) state: ChooseSelfVerificationModeState +) = ElementPreview { + ChooseSelfVerificationModeView( + state = state, + onUseAnotherDevice = {}, + onUseRecoveryKey = {}, + onResetKey = {}, + onLearnMore = {}, + ) +} diff --git a/features/ftue/impl/src/main/res/values-be/translations.xml b/features/ftue/impl/src/main/res/values-be/translations.xml index 413bad9132..6c10902ca6 100644 --- a/features/ftue/impl/src/main/res/values-be/translations.xml +++ b/features/ftue/impl/src/main/res/values-be/translations.xml @@ -1,7 +1,18 @@ + "Не можаце пацвердзіць?" + "Стварыць новы ключ аднаўлення" + "Пацвердзіце гэтую прыладу, каб наладзіць бяспечны абмен паведамленнямі." + "Пацвердзіце, што гэта вы" + "Выкарыстоўвайце іншую прыладу" + "Выкарыстоўваць ключ аднаўлення" + "Цяпер вы можаце бяспечна чытаць і адпраўляць паведамленні, і ўсе, з кім вы маеце зносіны ў чаце, таксама могуць давяраць гэтай прыладзе." + "Прылада праверана" + "Выкарыстоўвайце іншую прыладу" + "Чаканне на іншай прыладзе…" "Вы можаце змяніць налады пазней." "Дазвольце апавяшчэнні і ніколі не прапускайце іх" + "Увядзіце ключ аднаўлення" "Званкі, апытанні, пошук і многае іншае будзе дададзена пазней у гэтым годзе." "Гісторыя паведамленняў для зашыфраваных пакояў пакуль недаступна." "Мы будзем рады пачуць вашае меркаванне, паведаміце нам аб гэтым праз старонку налад." diff --git a/features/ftue/impl/src/main/res/values-bg/translations.xml b/features/ftue/impl/src/main/res/values-bg/translations.xml index 22370ab1c5..180c2bb7c3 100644 --- a/features/ftue/impl/src/main/res/values-bg/translations.xml +++ b/features/ftue/impl/src/main/res/values-bg/translations.xml @@ -2,6 +2,7 @@ "Можете да промените настройките си по-късно." "Разрешете известията и никога не пропускайте съобщение" + "Въвеждане на ключ за възстановяване" "Хронологията на съобщенията за шифровани стаи все още не е налична." "Добре дошли в %1$s!" diff --git a/features/ftue/impl/src/main/res/values-cs/translations.xml b/features/ftue/impl/src/main/res/values-cs/translations.xml index bc262cf2b4..9d852feed6 100644 --- a/features/ftue/impl/src/main/res/values-cs/translations.xml +++ b/features/ftue/impl/src/main/res/values-cs/translations.xml @@ -1,7 +1,18 @@ + "Nemůžete potvrdit?" + "Vytvoření nového klíče pro obnovení" + "Ověřte toto zařízení a nastavte zabezpečené zasílání zpráv." + "Potvrďte, že jste to vy" + "Použít jiné zařízení" + "Použít klíč pro obnovení" + "Nyní můžete bezpečně číst nebo odesílat zprávy, a kdokoli, s kým chatujete, může tomuto zařízení důvěřovat." + "Zařízení ověřeno" + "Použít jiné zařízení" + "Čekání na jiném zařízení…" "Nastavení můžete později změnit." "Povolte oznámení a nezmeškejte žádnou zprávu" + "Zadejte klíč pro obnovení" "Hovory, hlasování, vyhledávání a další budou přidány koncem tohoto roku." "Historie zpráv šifrovaných místností nebude v této aktualizaci k dispozici." "Rádi bychom se od vás dozvěděli, co si o tom myslíte, dejte nám vědět prostřednictvím stránky s nastavením." diff --git a/features/ftue/impl/src/main/res/values-de/translations.xml b/features/ftue/impl/src/main/res/values-de/translations.xml index 151500adda..731912888a 100644 --- a/features/ftue/impl/src/main/res/values-de/translations.xml +++ b/features/ftue/impl/src/main/res/values-de/translations.xml @@ -1,7 +1,18 @@ + "Sie können es nicht bestätigen?" + "Erstelle einen neuen Wiederherstellungsschlüssel" + "Verifiziere dieses Gerät, um sicheres Messaging einzurichten." + "Bestätige, dass du es bist" + "Ein anderes Gerät verwenden" + "Wiederherstellungsschlüssel verwenden" + "Du kannst nun verschlüsselte Nachrichten lesen oder versenden." + "Gerät verifiziert" + "Ein anderes Gerät verwenden" + "Bitte warten bis das andere Gerät bereit ist." "Du kannst deine Einstellungen später ändern." "Erlaube Benachrichtigungen und verpasse keine Nachricht" + "Wiederherstellungsschlüssel eingeben" "Anrufe, Umfragen, Suchfunktionen und mehr werden im Laufe des Jahres hinzugefügt." "Der Nachrichtenverlauf für verschlüsselte Räume wird in diesem Update nicht verfügbar sein." "Wir würden uns freuen, von dir zu hören. Teile uns deine Meinung über die Einstellungsseite mit." diff --git a/features/ftue/impl/src/main/res/values-el/translations.xml b/features/ftue/impl/src/main/res/values-el/translations.xml index 8c384bb8f4..82686850eb 100644 --- a/features/ftue/impl/src/main/res/values-el/translations.xml +++ b/features/ftue/impl/src/main/res/values-el/translations.xml @@ -1,7 +1,18 @@ + "Δεν μπορείς να επιβεβαιώσεις;" + "Δημιουργία νέου κλειδιού ανάκτησης" + "Επαλήθευσε αυτήν τη συσκευή για να ρυθμίσεις την ασφαλή επικοινωνία." + "Επιβεβαίωσε ότι είσαι εσύ" + "Χρήση άλλης συσκευής" + "Χρήση κλειδιού ανάκτησης" + "Τώρα μπορείς να διαβάζεις ή να στέλνεις μηνύματα με ασφάλεια και επίσης μπορεί να εμπιστευτεί αυτήν τη συσκευή οποιοσδήποτε με τον οποίο συνομιλείς." + "Επαληθευμένη συσκευή" + "Χρήση άλλης συσκευής" + "Αναμονή σε άλλη συσκευή…" "Μπορείς να αλλάξεις τις ρυθμίσεις σου αργότερα." "Επέτρεψε τις ειδοποιήσεις και μην χάσεις ούτε ένα μήνυμα" + "Εισαγωγή κλειδιού ανάκτησης" "Κλήσεις, δημοσκοπήσεις, αναζήτηση και άλλα, θα προστεθούν αργότερα φέτος." "Το ιστορικό μηνυμάτων για κρυπτογραφημένα δωμάτια δεν είναι ακόμα διαθέσιμο." "Θα θέλαμε να ακούσουμε τη γνώμη σου, πες μας τη γνώμη σου μέσω της σελίδας ρυθμίσεων." diff --git a/features/ftue/impl/src/main/res/values-es/translations.xml b/features/ftue/impl/src/main/res/values-es/translations.xml index 6a3224c531..20338b999f 100644 --- a/features/ftue/impl/src/main/res/values-es/translations.xml +++ b/features/ftue/impl/src/main/res/values-es/translations.xml @@ -1,7 +1,18 @@ + "¿No puedes confirmar?" + "Crear una nueva clave de recuperación" + "Verifica este dispositivo para configurar la mensajería segura." + "Confirma que eres tú" + "Usar otro dispositivo" + "Usar clave de recuperación" + "Ahora puedes leer o enviar mensajes de forma segura y cualquier persona con la que chatees también puede confiar en este dispositivo." + "Dispositivo verificado" + "Usar otro dispositivo" + "Esperando en otro dispositivo…" "Puedes cambiar la configuración más tarde." "Activa las notificaciones y nunca te pierdas un mensaje" + "Introduzca la clave de recuperación" "Las llamadas, las encuestas, la búsqueda y más se agregarán más adelante este año." "El historial de mensajes de las salas cifradas aún no está disponible." "Nos encantaría saber de ti, haznos saber lo que piensas a través de la página de configuración." diff --git a/features/ftue/impl/src/main/res/values-et/translations.xml b/features/ftue/impl/src/main/res/values-et/translations.xml index 83f6cb93fa..09762a4f63 100644 --- a/features/ftue/impl/src/main/res/values-et/translations.xml +++ b/features/ftue/impl/src/main/res/values-et/translations.xml @@ -1,7 +1,18 @@ + "Kas kinnitamine pole võimalik?" + "Loo uus taastevõti" + "Krüptitud sõnumivahetuse tagamiseks verifitseeri see seade." + "Kinnita, et see oled sina" + "Kasuta teist seadet" + "Kasuta taastevõtit" + "Nüüd saad saata või lugeda sõnumeid turvaliselt ning kõik sinu vestluspartnerid võivad usaldada seda seadet." + "Seade on verifitseeritud" + "Kasuta teist seadet" + "Ootame teise seadme järgi…" "Sa võid seadistusi hiljem alati muuta." "Luba teavitused ja kunagi ei jää sul sõnumid märkamata" + "Sisesta taastevõti" "Kõned, küsitlused, otsing ja palju muud lisanduvad hiljem selle aasta jooksul." "Krüptitud jututubade sõnumite ajalugu pole veel saadaval." "Me soovime teada mida sa arvad. Seadistuste lehel olevast valikust võid saata meile oma kommentaare." diff --git a/features/ftue/impl/src/main/res/values-eu/translations.xml b/features/ftue/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..2dcdff7330 --- /dev/null +++ b/features/ftue/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,21 @@ + + + "Ezin duzu baieztatu?" + "Sortu berreskuratze-gako berria" + "Egiaztatu gailua mezularitza segurua konfiguratzeko." + "Berretsi zure identitatea" + "Erabili beste gailu bat" + "Erabili berreskuratze-gakoa" + "Orain mezuak modu seguruan irakurri edo bidal ditzakezu, eta txateatzen duzun edonor ere fida daiteke gailu honetaz." + "Gailua egiaztatu da" + "Erabili beste gailu bat" + "Beste gailuaren zain…" + "Geroago alda ditzakezu ezarpenak." + "Baimendu jakinarazpenak eta ez galdu inoiz mezurik" + "Sartu berreskuratze-gakoa" + "Deiak, inkestak, bilaketa eta gehiago gehituko dira aurten." + "Enkriptatutako geletarako mezuen historia ez dago oraindik erabilgarri." + "Goazen!" + "Hona hemen jakin beharrekoa:" + "Ongi etorri %1$s(e)ra!" + diff --git a/features/ftue/impl/src/main/res/values-fa/translations.xml b/features/ftue/impl/src/main/res/values-fa/translations.xml index 43d968b7c5..c8de5e85ad 100644 --- a/features/ftue/impl/src/main/res/values-fa/translations.xml +++ b/features/ftue/impl/src/main/res/values-fa/translations.xml @@ -1,7 +1,18 @@ + "نمی‌توانید تأیید کنید؟" + "ایجاد کلید بازیابی جدید" + "تأیید این افزاره برای برپایی پیام‌رسانی امن." + "تأیید هویتتان" + "استفاده از افزاره‌ای دیگر" + "استفاده از کلید بازیابی" + "اکنون می‌توانید پیام‌ها را به صورت امن فرستاده و بگیرید و هرکسی که با او گپ می‌زنید نیز می‌تواند به این افزاره اعتماد کند." + "افزاره تأیید شده" + "استفاده از افزاره‌ای دیگر" + "منتظر افزارهٔ دیگر…" "می‌توانید بعداً تنظیماتتان را تغییر دهید." "اجازه به آگاهی‌ها و از دست ندادن پیام‌ها" + "ورود کلید بازیابی" "بزن بریم!" "چیزهایی که باید بدانید:" "به %1$s خوش آمدید!" diff --git a/features/ftue/impl/src/main/res/values-fi/translations.xml b/features/ftue/impl/src/main/res/values-fi/translations.xml index f04854ffef..3f3110bcdf 100644 --- a/features/ftue/impl/src/main/res/values-fi/translations.xml +++ b/features/ftue/impl/src/main/res/values-fi/translations.xml @@ -1,7 +1,18 @@ + "Etkö voi vahvistaa?" + "Luo uusi palautusavain" + "Vahvista tämä laite suojattua viestintää varten." + "Vahvista identiteettisi" + "Käytä toista laitetta" + "Käytä palautusavainta" + "Nyt voit lukea ja lähettää viestejä turvallisesti, ja kaikki, joiden kanssa keskustelet, voivat myös luottaa tähän laitteeseen." + "Laite vahvistettu" + "Käytä toista laitetta" + "Odotetaan toista laitetta…" "Voit muuttaa asetuksia myöhemmin." "Salli ilmoitukset ja älä koskaan missaa viestejä" + "Syötä palautusavain" "Puhelut, kyselyt, haku ja paljon muuta lisätään myöhemmin tänä vuonna." "Salattujen huoneiden viestihistoria ei ole vielä käytettävissä." "Haluaisimme kuulla mielipiteesi, kerro mitä mieltä olet asetuksien kautta." diff --git a/features/ftue/impl/src/main/res/values-fr/translations.xml b/features/ftue/impl/src/main/res/values-fr/translations.xml index 9fc11413cb..0439601447 100644 --- a/features/ftue/impl/src/main/res/values-fr/translations.xml +++ b/features/ftue/impl/src/main/res/values-fr/translations.xml @@ -1,7 +1,18 @@ + "Confirmation impossible ?" + "Créer une nouvelle clé de récupération" + "Vérifier cette session pour configurer votre messagerie sécurisée." + "Confirmez votre identité" + "Utiliser une autre session" + "Utiliser la clé de récupération" + "Vous pouvez désormais lire ou envoyer des messages en toute sécurité, et toute personne avec qui vous discutez peut également faire confiance à cette session." + "Session vérifiée" + "Utiliser une autre session" + "En attente d’une autre session…" "Vous pourrez modifier vos paramètres ultérieurement." "Autorisez les notifications et ne manquez aucun message" + "Utiliser la clé de récupération" "Les appels, les sondages, les recherches et plus encore seront ajoutés plus tard cette année." "L’historique des messages pour les salons chiffrés ne sera pas disponible dans cette mise à jour." "N’hésitez pas à nous faire part de vos commentaires via l’écran des paramètres." diff --git a/features/ftue/impl/src/main/res/values-hu/translations.xml b/features/ftue/impl/src/main/res/values-hu/translations.xml index 6a7acab0a4..5dfdedd4b0 100644 --- a/features/ftue/impl/src/main/res/values-hu/translations.xml +++ b/features/ftue/impl/src/main/res/values-hu/translations.xml @@ -1,7 +1,18 @@ + "Nem tudja megerősíteni?" + "Új helyreállítási kulcs létrehozása" + "A biztonságos üzenetkezelés beállításához ellenőrizze ezt az eszközt." + "Erősítse meg, hogy Ön az" + "Másik eszköz használata" + "Helyreállítási kulcs használata" + "Mostantól biztonságosan olvashat vagy küldhet üzeneteket, és bármelyik csevegőpartnere megbízhat ebben az eszközben." + "Eszköz ellenőrizve" + "Másik eszköz használata" + "Várakozás a másik eszközre…" "A beállításokat később is módosíthatja." "Értesítések engedélyezése, hogy soha ne maradjon le egyetlen üzenetről sem" + "Adja meg a helyreállítási kulcsot" "A hívások, szavazások, keresések és egyebek az év további részében kerülnek hozzáadásra." "A titkosított szobák üzenetelőzményei nem lesznek elérhetők ebben a frissítésben." "Szeretnénk hallani a véleményét, ossza meg velünk a beállítások oldalon." diff --git a/features/ftue/impl/src/main/res/values-in/translations.xml b/features/ftue/impl/src/main/res/values-in/translations.xml index 1004f8a643..5325b7f58d 100644 --- a/features/ftue/impl/src/main/res/values-in/translations.xml +++ b/features/ftue/impl/src/main/res/values-in/translations.xml @@ -1,7 +1,18 @@ + "Tidak dapat mengonfirmasi?" + "Buat kunci pemulihan baru" + "Verifikasi perangkat ini untuk menyiapkan perpesanan aman." + "Konfirmasi bahwa ini Anda" + "Gunakan perangkat lain" + "Gunakan kunci pemulihan" + "Sekarang Anda dapat membaca atau mengirim pesan dengan aman, dan siapa pun yang mengobrol dengan Anda juga dapat mempercayai perangkat ini." + "Perangkat terverifikasi" + "Gunakan perangkat lain" + "Menunggu di perangkat lain…" "Anda dapat mengubah pengaturan Anda nanti." "Izinkan pemberitahuan dan jangan pernah melewatkan pesan" + "Masukkan kunci pemulihan" "Panggilan, pemungutan suara, pencarian, dan lainnya akan ditambahkan di tahun ini." "Riwayat pesan untuk ruangan terenkripsi tidak akan tersedia dalam pembaruan ini." "Kami ingin mendengar dari Anda, beri tahu kami pendapat Anda melalui halaman pengaturan." diff --git a/features/ftue/impl/src/main/res/values-it/translations.xml b/features/ftue/impl/src/main/res/values-it/translations.xml index eaff5216dc..3992acd63e 100644 --- a/features/ftue/impl/src/main/res/values-it/translations.xml +++ b/features/ftue/impl/src/main/res/values-it/translations.xml @@ -1,7 +1,18 @@ + "Non puoi confermare?" + "Crea una nuova chiave di recupero" + "Verifica questo dispositivo per segnare i tuoi messaggi come sicuri." + "Conferma la tua identità" + "Usa un altro dispositivo" + "Usa la chiave di recupero" + "Ora puoi leggere o inviare messaggi in tutta sicurezza e anche chi chatta con te può fidarsi di questo dispositivo." + "Dispositivo verificato" + "Usa un altro dispositivo" + "In attesa sull\'altro dispositivo…" "Potrai modificare le tue impostazioni in seguito." "Consenti le notifiche e non perdere mai un messaggio" + "Inserisci la chiave di recupero" "Chiamate, sondaggi, ricerche e altro ancora saranno aggiunti nel corso dell\'anno." "La cronologia dei messaggi per le stanze crittografate non è ancora disponibile." "Ci piacerebbe sentire il tuo parere, facci sapere cosa ne pensi tramite la pagina delle impostazioni." diff --git a/features/ftue/impl/src/main/res/values-ka/translations.xml b/features/ftue/impl/src/main/res/values-ka/translations.xml index 04aacccf83..f8ae896aba 100644 --- a/features/ftue/impl/src/main/res/values-ka/translations.xml +++ b/features/ftue/impl/src/main/res/values-ka/translations.xml @@ -1,7 +1,14 @@ + "ახალი აღდგენის გასაღების შექმნა" + "დაადასტურეთ ეს მოწყობილობა უსაფრთხო მიმოწერისათვის." + "დაამტკიცეთ თქვენი პიროვნება" + "ახლა თქვენ შეძლებთ შეტყობინებების წაკითხვას ან გაგზავნას უსაფრთხოდ, სხვა მომხმარებლებსაც შეუძლიათ ამ მოწყობილობას ენდონ." + "მოწყობილობა დადასტურებულია" + "ველოდებით სხვა მოწყობილობას…" "თქვენ შეგიძლიათ შეცვალოთ თქვენი პარამეტრები მოგვიანებით." "ყველა შეტყობინებაზე შეტყობინებების მიღება" + "შეიყვანეთ აღდგენის გასაღები" "ზარები, გამოკითხვები, ძიება და სხვა დაემატება ამ წლის ბოლოს." "დაშიფრული ოთახებისთვის შეტყობინებების ისტორია ჯერ არ არის ხელმისაწვდომი." "ჩვენ სიამოვნებით მოვისმინოთ თქვენგან, შეგვატყობინეთ რას ფიქრობთ პარამეტრების გვერდზე." diff --git a/features/ftue/impl/src/main/res/values-nb/translations.xml b/features/ftue/impl/src/main/res/values-nb/translations.xml index 3f317d0342..61810952ba 100644 --- a/features/ftue/impl/src/main/res/values-nb/translations.xml +++ b/features/ftue/impl/src/main/res/values-nb/translations.xml @@ -1,7 +1,18 @@ + "Kan du ikke bekrefte?" + "Opprett en ny gjenopprettingsnøkkel" + "Verifiser denne enheten for å sette opp sikker meldingsutveksling." + "Bekreft identiteten din" + "Bruk en annen enhet" + "Bruk gjenopprettingsnøkkel" + "Nå kan du lese eller sende meldinger på en sikker måte, og alle du chatter med kan også stole på denne enheten." + "Enhet verifisert" + "Bruk en annen enhet" + "Venter på en annen enhet…" "Du kan endre innstillingene dine senere." "Tillat varslinger og gå aldri glipp av en melding" + "Skriv inn gjenopprettingsnøkkel" "Samtaler, avstemninger, søk og mer vil bli lagt til senere i år." "Meldingshistorikk for krypterte rom er ikke tilgjengelig ennå." "Vi vil gjerne høre fra deg, så la oss få vite hva du synes via innstillingssiden." diff --git a/features/ftue/impl/src/main/res/values-nl/translations.xml b/features/ftue/impl/src/main/res/values-nl/translations.xml index 1975f26467..459b003e53 100644 --- a/features/ftue/impl/src/main/res/values-nl/translations.xml +++ b/features/ftue/impl/src/main/res/values-nl/translations.xml @@ -1,7 +1,18 @@ + "Kan ik dit niet bevestigen?" + "Maak een nieuwe herstelsleutel" + "Verifieer dit apparaat om beveiligde berichten in te stellen." + "Bevestig dat jij het bent" + "Gebruik een ander apparaat" + "Gebruik de herstelsleutel" + "Nu kun je veilig berichten lezen of verzenden, en iedereen met wie je chat kan dit apparaat ook vertrouwen." + "Apparaat geverifieerd" + "Gebruik een ander apparaat" + "Wachten op ander apparaat…" "Je kunt je instellingen later wijzigen." "Sta meldingen toe en mis nooit meer een bericht" + "Voer herstelsleutel in" "Oproepen, peilingen, zoekopdrachten en meer zullen later dit jaar worden toegevoegd." "Berichtgeschiedenis voor versleutelde kamers is nog niet beschikbaar." "We horen graag van je, laat ons weten wat je ervan vindt via de instellingenpagina." diff --git a/features/ftue/impl/src/main/res/values-pl/translations.xml b/features/ftue/impl/src/main/res/values-pl/translations.xml index b1c1909ac2..682bae9cf9 100644 --- a/features/ftue/impl/src/main/res/values-pl/translations.xml +++ b/features/ftue/impl/src/main/res/values-pl/translations.xml @@ -1,7 +1,18 @@ + "Nie możesz potwierdzić?" + "Utwórz nowy klucz przywracania" + "Zweryfikuj to urządzenie, aby skonfigurować bezpieczne przesyłanie wiadomości." + "Potwierdź, że to Ty" + "Użyj innego urządzenia" + "Użyj klucza przywracania" + "Teraz możesz bezpiecznie czytać i wysyłać wiadomości, każdy z kim czatujesz również może ufać temu urządzeniu." + "Urządzenie zweryfikowane" + "Użyj innego urządzenia" + "Oczekiwanie na inne urządzenie…" "Możesz zmienić ustawienia później." "Zezwól na powiadomienia i nie przegap żadnej wiadomości" + "Wprowadź klucz przywracania" "Połączenia, ankiety, wyszukiwanie i inne zostaną dodane później w tym roku." "Historia wiadomości dla pokoi szyfrowanych nie jest jeszcze dostępna." "Chętnie poznamy Twoją opinię. Daj nam znać, co myślisz na stronie ustawień." diff --git a/features/ftue/impl/src/main/res/values-pt-rBR/translations.xml b/features/ftue/impl/src/main/res/values-pt-rBR/translations.xml index 1528027bd4..defb5f71d6 100644 --- a/features/ftue/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/ftue/impl/src/main/res/values-pt-rBR/translations.xml @@ -1,7 +1,11 @@ + "Usar outro dispositivo" + "Dispositivo verificado" + "Usar outro dispositivo" "Você pode alterar suas configurações mais tarde." "Permita notificações e nunca perca uma mensagem" + "Insira a chave de recuperação" "Chamadas, enquetes, pesquisa e muito mais serão adicionadas ainda este ano." "O histórico de mensagens para salas criptografadas ainda não está disponível." "Adoraríamos ouvir sua opinião. Deixe-nos saber o que você pensa através da página de configurações." diff --git a/features/ftue/impl/src/main/res/values-pt/translations.xml b/features/ftue/impl/src/main/res/values-pt/translations.xml index 8ff79c3b64..d96caa5065 100644 --- a/features/ftue/impl/src/main/res/values-pt/translations.xml +++ b/features/ftue/impl/src/main/res/values-pt/translations.xml @@ -1,7 +1,18 @@ + "Não é possível confirmar?" + "Criar uma nova chave de recuperação" + "Verifica este dispositivo para configurar o envio seguro de mensagens." + "Confirma que és tu" + "Utilizar outro dispositivo" + "Utilizar chave de recuperação" + "Agora podes ler ou enviar mensagens de forma segura, e qualquer pessoa com quem converses também pode confiar neste dispositivo." + "Dispositivo verificado" + "Utilizar outro dispositivo" + "A aguardar por outros dispositivos…" "Podes alterar as tuas definições mais tarde." "Permite as notificações e nunca percas uma mensagem" + "Insere a chave de recuperação" "Chamadas, sondagens, pesquisa e mais funcionalidades vão ser adicionadas ao longo do ano." "O histórico de mensagens em salas cifradas ainda não está disponível." "Gostaríamos de ouvir a tua opinião, diz-nos o que pensas através da página de configurações." diff --git a/features/ftue/impl/src/main/res/values-ro/translations.xml b/features/ftue/impl/src/main/res/values-ro/translations.xml index b89537903d..4d2c9febae 100644 --- a/features/ftue/impl/src/main/res/values-ro/translations.xml +++ b/features/ftue/impl/src/main/res/values-ro/translations.xml @@ -1,7 +1,18 @@ + "Nu puteți confirma?" + "Creați o nouă cheie de recuperare" + "Verificați acest dispozitiv pentru a configura mesagerie securizată." + "Confirmați că sunteți dumneavoastră" + "Utilizați un alt dispozitiv" + "Utilizați cheia de recuperare" + "Acum puteți citi sau trimite mesaje în siguranță, iar oricine cu care conversați poate avea încredere în acest dispozitiv." + "Dispozitiv verificat" + "Utilizați un alt dispozitiv" + "Se așteaptă celălalt dispozitiv…" "Puteți modifica setările mai târziu." "Permiteți notificările și nu pierdeți niciodată un mesaj" + "Introduceți cheia de recuperare" "Apelurile, sondajele, căutare și multe altele vor fi adăugate în cursul acestui an." "Istoricul mesajelor pentru camerele criptate nu va fi disponibil în această actualizare." "Ne-ar plăcea să auzim de la dumneavoastră, spuneți-ne ce părere aveți prin intermediul paginii de setări." diff --git a/features/ftue/impl/src/main/res/values-ru/translations.xml b/features/ftue/impl/src/main/res/values-ru/translations.xml index 7a5ed89a21..d287e65640 100644 --- a/features/ftue/impl/src/main/res/values-ru/translations.xml +++ b/features/ftue/impl/src/main/res/values-ru/translations.xml @@ -1,7 +1,18 @@ + "Не можете подтвердить?" + "Создайте новый ключ восстановления" + "Подтвердите это устройство, чтобы настроить безопасный обмен сообщениями." + "Подтвердите, что это вы" + "Использовать другое устройство" + "Используйте recovery key" + "Теперь вы можете безопасно читать и отправлять сообщения, и все, с кем вы общаетесь в чате, также могут доверять этому устройству." + "Устройство проверено" + "Использовать другое устройство" + "Ожидание на другом устройстве…" "Вы можете изменить настройки позже." "Разрешите отправку уведомлений и ни одно сообщение не будет пропущено" + "Введите ключ восстановления" "Звонки, опросы, поиск и многое другое будут добавлены позже в этом году." "История сообщений для зашифрованных комнат в этом обновлении будет недоступна." "Мы будем рады услышать ваше мнение, сообщите нам об этом через страницу настроек." diff --git a/features/ftue/impl/src/main/res/values-sk/translations.xml b/features/ftue/impl/src/main/res/values-sk/translations.xml index d963dbd734..9f39ca6065 100644 --- a/features/ftue/impl/src/main/res/values-sk/translations.xml +++ b/features/ftue/impl/src/main/res/values-sk/translations.xml @@ -1,7 +1,18 @@ + "Nemôžete potvrdiť?" + "Vytvoriť nový kľúč na obnovenie" + "Ak chcete nastaviť zabezpečené správy, overte toto zariadenie." + "Potvrďte, že ste to vy" + "Použite iné zariadenie" + "Použiť kľúč na obnovenie" + "Teraz môžete bezpečne čítať alebo odosielať správy a tomuto zariadeniu môže dôverovať aj ktokoľvek, s kým konverzujete." + "Zariadenie overené" + "Použite iné zariadenie" + "Čaká sa na druhom zariadení…" "Svoje nastavenia môžete neskôr zmeniť." "Povoľte oznámenia a nikdy nezmeškajte žiadnu správu" + "Zadajte kľúč na obnovenie" "Hovory, ankety, vyhľadávanie a ďalšie funkcie pribudnú neskôr v tomto roku." "História správ pre zašifrované miestnosti nebude v tejto aktualizácii k dispozícii." "Radi by sme od vás počuli, dajte nám vedieť, čo si myslíte, prostredníctvom stránky nastavení." diff --git a/features/ftue/impl/src/main/res/values-sv/translations.xml b/features/ftue/impl/src/main/res/values-sv/translations.xml index 31823cfaab..ffaf682393 100644 --- a/features/ftue/impl/src/main/res/values-sv/translations.xml +++ b/features/ftue/impl/src/main/res/values-sv/translations.xml @@ -1,7 +1,18 @@ + "Kan du inte bekräfta?" + "Skapa en ny återställningsnyckel" + "Verifiera den här enheten för att konfigurera säkra meddelanden." + "Bekräfta att det är du" + "Använd en annan enhet" + "Använd återställningsnyckel" + "Nu kan du läsa eller skicka meddelanden säkert, och alla du chattar med kan också lita på den här enheten." + "Enhet verifierad" + "Använd en annan enhet" + "Väntar på annan enhet …" "Du kan ändra dina inställningar senare." "Tillåt aviseringar och missa aldrig ett meddelande" + "Ange återställningsnyckel" "Samtal, omröstningar, sökning och mer kommer att läggas till senare i år." "Meddelandehistorik för krypterade rum är inte tillgänglig än." "Vi vill gärna höra från dig, låt oss veta vad du tycker via inställningssidan." diff --git a/features/ftue/impl/src/main/res/values-tr/translations.xml b/features/ftue/impl/src/main/res/values-tr/translations.xml index a653e93624..dbeb65832c 100644 --- a/features/ftue/impl/src/main/res/values-tr/translations.xml +++ b/features/ftue/impl/src/main/res/values-tr/translations.xml @@ -1,7 +1,18 @@ + "Onaylayamıyor musunuz?" + "Yeni bir kurtarma anahtarı oluştur" + "Güvenli mesajlaşmayı ayarlamak için bu cihazı doğrulayın." + "Kimliğinizi doğrulayın" + "Başka bir cihaz kullan" + "Kurtarma anahtarı kullan" + "Artık mesajları güvenli bir şekilde okuyabilir veya gönderebilirsiniz ve sohbet ettiğiniz herkes de bu cihaza güvenebilir." + "Cihaz doğrulandı" + "Başka bir cihaz kullan" + "Diğer cihazda bekleniyor…" "Ayarlarınızı daha sonra değiştirebilirsiniz." "Bildirimlere izin verin ve hiçbir mesajı kaçırmayın" + "Kurtarma anahtarını girin" "Çağrılar, anketler, arama ve daha fazlası bu yıl içinde eklenecek." "Şifrelenmiş odalar için mesaj geçmişi henüz mevcut değil." "Düşüncelerinizi bizimle paylaşmanızı çok isteriz. Ayarlar sayfasından düşüncelerinizi bize iletin." diff --git a/features/ftue/impl/src/main/res/values-uk/translations.xml b/features/ftue/impl/src/main/res/values-uk/translations.xml index 920f6d3d1c..6dc0056149 100644 --- a/features/ftue/impl/src/main/res/values-uk/translations.xml +++ b/features/ftue/impl/src/main/res/values-uk/translations.xml @@ -1,7 +1,18 @@ + "Не можете підтвердити?" + "Створити новий ключ відновлення" + "Верифікуйте цей пристрій, щоб налаштувати безпечний обмін повідомленнями." + "Підтвердьте, що це ви" + "Використовуйте інший пристрій" + "Використовуйте ключ відновлення" + "Тепер ви можете безпечно читати або надсилати повідомлення, і кожен, з ким ви спілкуєтесь, також може довіряти цьому пристрою." + "Пристрій перевірено" + "Використовуйте інший пристрій" + "Чекає на інше пристрій…" "Ви можете змінити свої налаштування пізніше." "Дозволити сповіщення і ніколи не пропускати повідомлення" + "Введіть ключ відновлення" "Виклики, опитування, пошук тощо будуть додані пізніше цього року." "Історія повідомлень для зашифрованих кімнат ще недоступна." "Ми хотіли б почути вас, розкажіть нам ваші враження та ідеї щодо застосунку на сторінці налаштувань." diff --git a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml index 34fb4fbe35..5658f30616 100644 --- a/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/ftue/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,7 +1,18 @@ + "無法確認?" + "建立新的復原金鑰" + "驗證這部裝置以設定安全通訊。" + "確認這是你本人" + "使用另一部裝置" + "使用復原金鑰" + "您可以安全地讀取和發送訊息了,與您聊天的人也可以信任這部裝置。" + "裝置已驗證" + "使用另一部裝置" + "正在等待其他裝置…" "您稍後仍可變更設定。" "允許通知,永遠不會錯誤任何訊息" + "輸入復原金鑰" "通話、投票、搜尋等更多功能將在今年登場。" "在這次的更新,您無法查看聊天室內被加密的歷史訊息。" "我們很樂意聽取您的意見,請到設定頁面告訴我們您的想法。" diff --git a/features/ftue/impl/src/main/res/values-zh/translations.xml b/features/ftue/impl/src/main/res/values-zh/translations.xml index c98be66867..88b8b4252e 100644 --- a/features/ftue/impl/src/main/res/values-zh/translations.xml +++ b/features/ftue/impl/src/main/res/values-zh/translations.xml @@ -1,7 +1,18 @@ + "无法确认?" + "创建新的恢复密钥" + "验证此设备以开始安全地收发消息。" + "确认这是你" + "使用其他设备" + "使用恢复密钥" + "现在,您可以安全地阅读或发送消息,与您聊天的人也会信任此设备。" + "设备已验证" + "使用其他设备" + "正在等待其他设备……" "您可以稍后更改设置。" "允许通知,绝不错过任何消息" + "输入恢复密钥" "今年晚些时候将增加通话、投票、搜索等功能。" "加密聊天室的消息历史记录尚不可用。" "我们很乐意听取您的意见,请通过设置页面告诉我们您的想法。" diff --git a/features/ftue/impl/src/main/res/values/localazy.xml b/features/ftue/impl/src/main/res/values/localazy.xml index 3e8c86b761..47214a9304 100644 --- a/features/ftue/impl/src/main/res/values/localazy.xml +++ b/features/ftue/impl/src/main/res/values/localazy.xml @@ -1,7 +1,18 @@ + "Can\'t confirm?" + "Create a new recovery key" + "Verify this device to set up secure messaging." + "Confirm your identity" + "Use another device" + "Use recovery key" + "Now you can read or send messages securely, and anyone you chat with can also trust this device." + "Device verified" + "Use another device" + "Waiting on other device…" "You can change your settings later." "Allow notifications and never miss a message" + "Enter recovery key" "Calls, polls, search and more will be added later this year." "Message history for encrypted rooms isn’t available yet." "We’d love to hear from you, let us know what you think via the settings page." 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 new file mode 100644 index 0000000000..3801001ed4 --- /dev/null +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModePresenterTest.kt @@ -0,0 +1,64 @@ +/* + * 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.ftue.impl.sessionverification.choosemode + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.logout.api.direct.DirectLogoutEvents +import io.element.android.features.logout.api.direct.DirectLogoutState +import io.element.android.features.logout.api.direct.aDirectLogoutState +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.encryption.RecoveryState +import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService +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 ChooseSessionVerificationModePresenterTest { + @Test + fun `initial state - is relayed from EncryptionService`() = runTest { + val encryptionService = FakeEncryptionService().apply { + // Is last device + emitIsLastDevice(true) + // Can enter recovery key + emitRecoveryState(RecoveryState.INCOMPLETE) + } + val presenter = createPresenter(encryptionService = encryptionService) + presenter.test { + awaitItem().run { + assertThat(isLastDevice).isTrue() + assertThat(canEnterRecoveryKey).isTrue() + assertThat(directLogoutState.logoutAction.isUninitialized()).isTrue() + } + } + } + + @Test + fun `sing out action triggers a direct logout`() = runTest { + val logoutEventRecorder = lambdaRecorder {} + val logoutPresenter = Presenter { + aDirectLogoutState(eventSink = logoutEventRecorder) + } + val presenter = createPresenter(directLogoutPresenter = logoutPresenter) + presenter.test { + val initial = awaitItem() + initial.eventSink(ChooseSelfVerificationModeEvent.SignOut) + + logoutEventRecorder.assertions().isCalledOnce().with(value(DirectLogoutEvents.Logout(ignoreSdkError = false))) + } + } + + private fun createPresenter( + encryptionService: FakeEncryptionService = FakeEncryptionService(), + directLogoutPresenter: Presenter = Presenter { aDirectLogoutState() } + ) = ChooseSelfVerificationModePresenter( + encryptionService = encryptionService, + directLogoutPresenter = directLogoutPresenter, + ) +} 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 new file mode 100644 index 0000000000..b89e3e42bd --- /dev/null +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSessionVerificationModeViewTest.kt @@ -0,0 +1,94 @@ +/* + * 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.ftue.impl.sessionverification.choosemode + +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.features.ftue.impl.R +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +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 ChooseSessionVerificationModeViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on learn more invokes the expected callback`() { + ensureCalledOnce { callback -> + rule.setChooseSelfVerificationModeView( + aChooseSelfVerificationModeState(), + onLearnMoreClick = callback, + ) + rule.clickOn(CommonStrings.action_learn_more) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on use another device calls the callback`() { + ensureCalledOnce { callback -> + rule.setChooseSelfVerificationModeView( + aChooseSelfVerificationModeState(isLastDevice = false), + onUseAnotherDevice = callback, + ) + rule.clickOn(R.string.screen_identity_use_another_device) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on enter recovery key calls the callback`() { + ensureCalledOnce { callback -> + rule.setChooseSelfVerificationModeView( + aChooseSelfVerificationModeState(canEnterRecoveryKey = true), + onEnterRecoveryKey = callback, + ) + rule.clickOn(R.string.screen_session_verification_enter_recovery_key) + } + } + + @Config(qualifiers = "h1024dp") + @Test + fun `clicking on cannot confirm calls the reset keys callback`() { + ensureCalledOnce { callback -> + rule.setChooseSelfVerificationModeView( + aChooseSelfVerificationModeState(), + onResetKey = callback, + ) + rule.clickOn(R.string.screen_identity_confirmation_cannot_confirm) + } + } + + private fun AndroidComposeTestRule.setChooseSelfVerificationModeView( + state: ChooseSelfVerificationModeState, + onLearnMoreClick: () -> Unit = EnsureNeverCalled(), + onUseAnotherDevice: () -> Unit = EnsureNeverCalled(), + onResetKey: () -> Unit = EnsureNeverCalled(), + onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(), + ) { + setContent { + ChooseSelfVerificationModeView( + state = state, + onLearnMore = onLearnMoreClick, + onUseAnotherDevice = onUseAnotherDevice, + onResetKey = onResetKey, + onUseRecoveryKey = onEnterRecoveryKey, + ) + } + } +} 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 d4c60464b3..58876c3392 100644 --- a/features/invite/impl/src/main/res/values-cs/translations.xml +++ b/features/invite/impl/src/main/res/values-cs/translations.xml @@ -6,4 +6,8 @@ "Odmítnout chat" "Žádné pozvánky" "%1$s (%2$s) vás pozval(a)" + "Ano, odmítnout a zablokovat" + "Opravdu chcete odmítnout pozvánku do této místnosti? Tím také zabráníte tomu, aby vás %1$s kontaktoval(a) nebo pozval(a) do místností." + "Odmítnout pozvání a zablokovat" + "Odmítnout a zablokovat" 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 bb38811a7a..fb390c4354 100644 --- a/features/invite/impl/src/main/res/values-de/translations.xml +++ b/features/invite/impl/src/main/res/values-de/translations.xml @@ -6,4 +6,8 @@ "Einladung ablehnen" "Keine Einladungen" "%1$s (%2$s) hat dich eingeladen" + "Ja, ablehnen und blockieren" + "Sind Sie sicher, dass Sie die Einladung zu diesem Raum ablehnen möchten? Dadurch wird auch verhindert, dass %1$s Sie kontaktiert oder in Räume einlädt." + "Einladung ablehnen und blockieren" + "Ablehnen und blockieren" diff --git a/features/invite/impl/src/main/res/values-es/translations.xml b/features/invite/impl/src/main/res/values-es/translations.xml index 890df29447..c3660e35af 100644 --- a/features/invite/impl/src/main/res/values-es/translations.xml +++ b/features/invite/impl/src/main/res/values-es/translations.xml @@ -2,8 +2,12 @@ "¿Estás seguro de que quieres rechazar la invitación a unirte a %1$s?" "Rechazar la invitación" - "¿Estás seguro de que quieres rechazar este chat privado con%1$s?" + "¿Estás seguro de que quieres rechazar este chat privado con %1$s?" "Rechazar el chat" "Sin invitaciones" "%1$s (%2$s) te invitó" + "Sí, rechazar y bloquear" + "¿Estás seguro de que deseas rechazar la invitación para unirte a esta sala? Esto también impedirá que %1$s pueda contactar contigo o invitarte a salas." + "Rechazar invitación y bloquear" + "Rechazar y bloquear" 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 316ba75ef9..59a6b112c6 100644 --- a/features/invite/impl/src/main/res/values-et/translations.xml +++ b/features/invite/impl/src/main/res/values-et/translations.xml @@ -6,4 +6,8 @@ "Keeldu vestlusest" "Kutseid pole" "%1$s (%2$s) saatis sulle kutse" + "Jah, keeldu ja blokeeri" + "Kas sa oled kindel, et soovid keelduda kutsest sellesse jututuppa? Samaga kaob kasutajal %1$s võimalus sinuga suhelda ja saata sulle jututubade kutseid." + "Keeldu kutsest ja blokeeri" + "Keeldu ja blokeeri" diff --git a/features/invite/impl/src/main/res/values-eu/translations.xml b/features/invite/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..d60aeced77 --- /dev/null +++ b/features/invite/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,9 @@ + + + "Ziur %1$s(e)ra batzeko gonbidapena baztertu nahi duzula?" + "Baztertu gonbidapena" + "Ziur %1$s(r)en txat pribatua baztertu nahi duzula?" + "Baztertu txata" + "Ez dago gonbidapenik" + "%1$s(e)k (%2$s) gonbidatu zaitu" + 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 bf5b83a8c6..c97a8c709e 100644 --- a/features/invite/impl/src/main/res/values-fi/translations.xml +++ b/features/invite/impl/src/main/res/values-fi/translations.xml @@ -6,4 +6,8 @@ "Hylkää keskustelu" "Ei kutsuja" "%1$s (%2$s) kutsui sinut" + "Kyllä, hylkää ja estä" + "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ä" diff --git a/features/invite/impl/src/main/res/values-fr/translations.xml b/features/invite/impl/src/main/res/values-fr/translations.xml index 45c29b3572..eae8484897 100644 --- a/features/invite/impl/src/main/res/values-fr/translations.xml +++ b/features/invite/impl/src/main/res/values-fr/translations.xml @@ -6,4 +6,8 @@ "Refuser l’invitation" "Aucune invitation" "%1$s (%2$s) vous a invité(e)" + "Oui, refuser et bloquer" + "Ê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" 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 e7faddd8f9..1ba1b208b8 100644 --- a/features/invite/impl/src/main/res/values-hu/translations.xml +++ b/features/invite/impl/src/main/res/values-hu/translations.xml @@ -6,4 +6,8 @@ "Csevegés elutasítása" "Nincsenek meghívások" "%1$s (%2$s) meghívta" + "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" diff --git a/features/invite/impl/src/main/res/values-it/translations.xml b/features/invite/impl/src/main/res/values-it/translations.xml index 7da765090e..624fc02842 100644 --- a/features/invite/impl/src/main/res/values-it/translations.xml +++ b/features/invite/impl/src/main/res/values-it/translations.xml @@ -6,4 +6,8 @@ "Rifiuta l\'invito alla conversazione" "Nessun invito" "%1$s (%2$s) ti ha invitato" + "Sì, rifiuta e blocca" + "Sei sicuro di voler rifiutare l\'invito a entrare in questa stanza? Ciò impedirà a %1$s di contattarti o invitarti nuovamente in una stanza." + "Rifiuta invito e blocca" + "Rifiuta e blocca" 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 59b1fffb08..1e64bc42a4 100644 --- a/features/invite/impl/src/main/res/values-nb/translations.xml +++ b/features/invite/impl/src/main/res/values-nb/translations.xml @@ -6,4 +6,7 @@ "Avslå chat" "Ingen invitasjoner" "%1$s(%2$s) inviterte deg" + "Ja, avslå og blokker" + "Avslå invitasjon og blokker" + "Avslå og blokker" 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 95283e4cba..b507ee1e2c 100644 --- a/features/invite/impl/src/main/res/values-pl/translations.xml +++ b/features/invite/impl/src/main/res/values-pl/translations.xml @@ -6,4 +6,8 @@ "Odrzuć czat" "Brak zaproszeń" "%1$s (%2$s) zaprosił Cię" + "Tak, odrzuć i zablokuj" + "Czy na pewno chcesz odrzucić zaproszenie dołączenia do tego pokoju? %1$s nie będzie mógł się również z Tobą skontaktować, ani zaprosić Cię do pokoju." + "Odrzuć zaproszenie i zablokuj" + "Odrzuć i zablokuj" 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 a7f257261a..f71940012c 100644 --- a/features/invite/impl/src/main/res/values-pt/translations.xml +++ b/features/invite/impl/src/main/res/values-pt/translations.xml @@ -6,4 +6,8 @@ "Rejeitar conversa" "Sem convites" "%1$s (%2$s) convidou-te" + "Sim, recusar & bloquear" + "Tens a certeza de que queres recusar o convite para entrar nesta sala? Isto também evitará que %1$s te contacte ou te convide para salas." + "Recusar convite & bloquear" + "Recusar e bloquear" 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 b13ca06a9d..2a38a96e1b 100644 --- a/features/invite/impl/src/main/res/values-ru/translations.xml +++ b/features/invite/impl/src/main/res/values-ru/translations.xml @@ -6,4 +6,8 @@ "Отклонить чат" "Нет приглашений" "%1$s (%2$s) пригласил вас" + "Да, отклонить и заблокировать" + "Вы действительно хотите отклонить приглашение в эту комнате? Это также предотвратит %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 2875466fc7..5ad69a009c 100644 --- a/features/invite/impl/src/main/res/values-sk/translations.xml +++ b/features/invite/impl/src/main/res/values-sk/translations.xml @@ -6,4 +6,8 @@ "Odmietnuť konverzáciu" "Žiadne pozvánky" "%1$s (%2$s) vás pozval/a" + "Áno, odmietnuť a zablokovať" + "Ste si istí, že chcete odmietnuť pozvanie na vstup do tejto miestnosti? To tiež zabráni tomu, aby vás %1$s kontaktoval/a alebo vás pozval/a do miestností." + "Odmietnuť pozvánku a zablokovať" + "Odmietnuť a zablokovať" 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 6a091b096b..1145e80df0 100644 --- a/features/invite/impl/src/main/res/values-sv/translations.xml +++ b/features/invite/impl/src/main/res/values-sv/translations.xml @@ -6,4 +6,8 @@ "Avböj chatt" "Inga inbjudningar" "%1$s (%2$s) bjöd in dig" + "Ja, avvisa och blockera" + "Är du säker på att du vill avvisa inbjudan att gå med i det här rummet? Detta kommer också att hindra %1$s från att kontakta dig eller bjuda in dig till rum." + "Avvisa inbjudan och blockera" + "Avvisa och blockera" 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 09beb1f56a..7c60a22b8a 100644 --- a/features/invite/impl/src/main/res/values-tr/translations.xml +++ b/features/invite/impl/src/main/res/values-tr/translations.xml @@ -6,4 +6,8 @@ "Sohbeti reddet" "Davet Yok" "%1$s (%2$s) sizi davet etti" + "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 fa535213ba..2f09bf4ea8 100644 --- a/features/invite/impl/src/main/res/values-uk/translations.xml +++ b/features/invite/impl/src/main/res/values-uk/translations.xml @@ -6,4 +6,8 @@ "Відхилити бесіду" "Немає запрошень" "%1$s (%2$s) запрошує вас" + "Так, відхилити та заблокувати" + "Ви впевнені, що хочете відхилити запрошення приєднатися до цієї кімнати? Це також завадить %1$s зв\'язатися з вами або запрошувати вас в кімнати." + "Відхилити запрошення та заблокувати" + "Відхилити та заблокувати" diff --git a/features/invite/impl/src/main/res/values-zh-rTW/translations.xml b/features/invite/impl/src/main/res/values-zh-rTW/translations.xml index 4c7d99280f..90e2a840df 100644 --- a/features/invite/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/invite/impl/src/main/res/values-zh-rTW/translations.xml @@ -6,4 +6,8 @@ "拒絕聊天" "沒有邀請" "%1$s(%2$s)邀請您" + "是的,拒絕並封鎖" + "您確定要拒絕加入此聊天室的邀請嗎?這也會防止 %1$s 聯絡您或邀請您加入聊天室。" + "拒絕邀請並封鎖" + "拒絕並封鎖" 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 0b0c72d854..0e8c3e3581 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 @@ -85,7 +85,7 @@ fun JoinRoomView( ) { HeaderFooterPage( containerColor = Color.Transparent, - paddingValues = PaddingValues( + contentPadding = PaddingValues( horizontal = 16.dp, vertical = 32.dp ), diff --git a/features/joinroom/impl/src/main/res/values-es/translations.xml b/features/joinroom/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..b98536b1be --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,32 @@ + + + "Has sido vetado de esta sala por %1$s." + "Has sido vetado de esta sala" + "Motivo: %1$s." + "Cancelar solicitud" + "Sí, cancelar" + "¿Estás seguro de que deseas cancelar tu solicitud de unión a esta sala?" + "Cancelar solicitud de unión" + "Sí, rechazar y bloquear" + "¿Estás seguro de que deseas rechazar la invitación para unirte a esta sala? Esto también impedirá que %1$s pueda contactar contigo o invitarte a salas." + "Rechazar invitación y bloquear" + "Rechazar y bloquear" + "No se pudo unir a la sala." + "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" + "Es posible que necesites ser invitado o ser miembro de un espacio para poder unirte." + "Enviar solicitud de unión" + "Mensaje (opcional)" + "Recibirás una invitación para unirte a la sala si se acepta tu solicitud." + "Solicitud de unión enviada" + "No hemos podido mostrar la vista previa de la sala. Esto puede deberse a problemas de red o del servidor." + "No hemos podido mostrar la vista previa de esta sala" + "%1$s aún no admite los espacios. Puedes acceder a los espacios en la web." + "Todavía no se admiten los espacios" + "Haz clic en el botón que aparece a continuación y se notificará a un administrador de la sala. Podrás unirte a la conversación una vez que hayas sido aprobado." + "Debes ser miembro de esta sala para ver el historial de mensajes." + "¿Quieres unirte a esta sala?" + "La vista previa no está disponible" + diff --git a/features/joinroom/impl/src/main/res/values-eu/translations.xml b/features/joinroom/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..8e5cb2641a --- /dev/null +++ b/features/joinroom/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,15 @@ + + + "Utzi eskaera bertan behera" + "Bai, utzi bertan behera" + "Sartu gelan" + "Bidali batzeko eskaera" + "Mezua (aukerakoa)" + "Sartzeko eskaera bidali da" + "%1$s ez da oraindik guneekin bateragarria. Webgunean sar zaitezke guneetara." + "Oraindik ez da guneekin bateragarria" + "Klikatu beheko botoia eta gelako administratzaileari jakinaraziko zaio. Elkarrizketara batu ahal izango zara onartutakoan." + "Gela honetako kide izan behar zara mezuen historia ikusteko." + "Gela honetan sartu nahi?" + "Aurrebista ez dago erabilgarri" + 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 721628b60d..2a25587398 100644 --- a/features/joinroom/impl/src/main/res/values-fi/translations.xml +++ b/features/joinroom/impl/src/main/res/values-fi/translations.xml @@ -7,6 +7,10 @@ "Kyllä, peruuta" "Haluatko varmasti peruuttaa pyyntösi liittyä tähän huoneeseen?" "Peruuta liittymispyyntö" + "Kyllä, hylkää ja estä" + "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" @@ -17,6 +21,8 @@ "Viesti (valinnainen)" "Saat kutsun liittyä huoneeseen, jos pyyntösi hyväksytään." "Liittymispyyntö lähetetty" + "Emme voineet näyttää huoneen esikatselua. Tämä voi johtua verkko- tai palvelinongelmista." + "Emme voineet näyttää tämän huoneen esikatselua" "%1$s ei tue vielä tiloja. Voit käyttää tiloja selainversiolla." "Tiloja ei vielä tueta" "Paina alla olevaa nappia ja huoneen ylläpitäjä saa ilmoituksen. Voit liittyä keskusteluun kun pyyntösi on hyväksytty." 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 0c80a574e7..753b54159a 100644 --- a/features/joinroom/impl/src/main/res/values-it/translations.xml +++ b/features/joinroom/impl/src/main/res/values-it/translations.xml @@ -7,6 +7,10 @@ "Sì, annulla" "Sei sicuro di voler annullare la tua richiesta di accesso a questa stanza?" "Annulla la richiesta di accesso" + "Sì, rifiuta e blocca" + "Sei sicuro di voler rifiutare l\'invito a entrare in questa stanza? Ciò impedirà a %1$s di contattarti o invitarti nuovamente in una stanza." + "Rifiuta invito e blocca" + "Rifiuta e blocca" "L\'accesso alla stanza non è riuscito." "Questa stanza è solo su invito o potrebbero esserci delle restrizioni all\'accesso al livello dello spazio." "Dimentica questa stanza" @@ -17,6 +21,8 @@ "Messaggio (opzionale)" "Riceverai un invito a entrare nella stanza se la tua richiesta viene accettata." "Richiesta di accesso inviata" + "Non è stato possibile visualizzare l\'anteprima della stanza. Ciò può essere dovuto a problemi di rete o del server." + "Non è stato possibile visualizzare l\'anteprima di questa stanza" "%1$s non supporta ancora gli spazi. Puoi accedere agli spazi sul web." "Gli spazi non sono ancora supportati" "Clicca sul pulsante qui sotto e un amministratore della stanza riceverà una notifica. Potrai partecipare alla conversazione una volta approvato." 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 103b10201d..da5fa44dbb 100644 --- a/features/joinroom/impl/src/main/res/values-nb/translations.xml +++ b/features/joinroom/impl/src/main/res/values-nb/translations.xml @@ -1,6 +1,29 @@ + "Du ble utestengt fra dette rommet av %1$s." + "Du ble utestengt fra dette rommet" + "Årsak: %1$s." + "Avbryt forespørsel" + "Ja, avbryt" + "Er du sikker på at du vil kansellere forespørselen din om å bli med i dette rommet?" + "Avbryt forespørsel om å bli med" + "Ja, avslå og blokker" + "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" + "Du må kanskje bli invitert eller være medlem av et område for å bli med." + "Send forespørsel om å bli med" + "Melding (valgfritt)" + "Du vil motta en invitasjon til å bli med i rommet hvis forespørselen din blir akseptert." + "Forespørsel om å bli med sendt" "%1$s støtter ikke områder ennå. Du kan få tilgang til områder på nett." "Områder støttes ikke ennå" + "Klikk på knappen nedenfor, så vil en romadministrator bli varslet. Du vil kunne delta i samtalen når den er godkjent." + "Du må være medlem av dette rommet for å se meldingshistorikken." + "Vil du bli med i dette rommet?" + "Forhåndsvisning er ikke tilgjengelig" 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 be56b68031..a6aa79ebd9 100644 --- a/features/joinroom/impl/src/main/res/values-sv/translations.xml +++ b/features/joinroom/impl/src/main/res/values-sv/translations.xml @@ -1,16 +1,28 @@ + "Du bannades från det här rummet av %1$s." + "Du bannades från det här rummet" + "Anledning: %1$s." "Avbryt begäran" "Ja, avbryt" "Är du säker på att du vill avbryta din begäran om att gå med i det här rummet?" "Avbryt begäran om att gå med" + "Ja, avvisa och blockera" + "Är du säker på att du vill avvisa inbjudan att gå med i det här rummet? Detta kommer också att hindra %1$s från att kontakta dig eller bjuda in dig till rum." + "Avvisa inbjudan och blockera" + "Avvisa och blockera" + "Misslyckades att gå med i rummet." + "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" + "Du kan behöva bli inbjuden eller vara medlem i ett utrymme för att gå med." "Knacka för att gå med" "Meddelande (valfritt)" "Du kommer att få en inbjudan att gå med i rummet om din begäran accepteras." "Begäran om att gå med skickad" + "Vi kunde inte visa förhandsgranskningen av rummet. Detta kan bero på nätverks- eller serverproblem." + "Vi kunde inte visa förhandsgranskningen av rummet" "%1$s stöder inte utrymmen än. Du kan komma åt utrymmen på webben." "Utrymmen stöds inte ännu" "Klicka på knappen nedan så kommer en rumsadministratör att meddelas. Du kommer att kunna gå med i konversationen när den har godkänts." 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 30c2956a6b..b176a76e0f 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 @@ -1,14 +1,28 @@ + "您被 %1$s 禁止進入此聊天室。" + "您被禁止進入此聊天室" + "理由:%1$s。" "取消請求" "是的,取消" "您確定您想要取消加入此聊天室的請求嗎?" "取消加入請求" + "是的,拒絕並封鎖" + "您確定要拒絕加入此聊天室的邀請嗎?這也會防止 %1$s 聯絡您或邀請您加入聊天室。" + "拒絕邀請並封鎖" + "拒絕並封鎖" + "加入聊天室失敗。" + "此聊天室僅有受邀者才能進入,或是在空間層級有存取限制。" + "忘記此聊天室" + "您需要獲得邀請才能加入此聊天室" "加入聊天室" + "您可能需要被邀請成為空間的成員才能加入。" "傳送加入請求" "訊息(選擇性)" "若接受了您的請求,您將會收到加入聊天是的邀請。" "已傳送加入請求" + "我們無法顯示聊天室預覽。這可能是因為網路或伺服器問題所致。" + "我們無法顯示此聊天室的預覽" "%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 66914c9c89..ea9f74544d 100644 --- a/features/knockrequests/impl/src/main/res/values-es/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-es/translations.xml @@ -1,4 +1,36 @@ + "Sí, aceptar todas" + "¿Estás seguro de que quieres aceptar todas las solicitudes de unión?" + "Aceptar todas las solicitudes" + "Aceptar todas" + "No pudimos aceptar todas las solicitudes. ¿Quieres volver a intentarlo?" + "No se pudieron aceptar todas las solicitudes" + "Aceptando todas las solicitudes de unión" + "No pudimos aceptar esta solicitud. ¿Quieres volver a intentarlo?" + "No se pudo aceptar la solicitud" + "Aceptando solicitud de unión" + "Sí, rechazar y vetar" + "¿Estás seguro de que quieres rechazar y vetar a %1$s? Este usuario no podrá volver a solicitar acceso para unirse a esta sala." + "Rechazar y vetar el acceso" + "Rechazando y vetando acceso" + "Sí, rechazar" + "¿Estás seguro de que deseas rechazar la solicitud de %1$s para unirse a esta sala?" + "Rechazar el acceso" + "Rechazar y vetar" + "No pudimos rechazar esta solicitud. ¿Quieres volver a intentarlo?" + "No se pudo rechazar la solicitud" + "Rechazando solicitud de unión" + "Cuando alguien pida unirse a la sala, podrás ver su solicitud aquí." + "No hay ninguna solicitud de unión pendiente" + "Cargando solicitudes de unión…" + "Solicitudes de unión" + + "%1$s y %2$d más quieren unirse a esta sala" + "%1$s y otros %2$d más quieren unirse a esta sala" + + "Ver todo" "Aceptar" + "%1$s quiere unirse a esta sala" + "Ver" diff --git a/features/knockrequests/impl/src/main/res/values-eu/translations.xml b/features/knockrequests/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..5f4585070c --- /dev/null +++ b/features/knockrequests/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,19 @@ + + + "Bai, onartu guztiak" + "Onartu eskaera guztiak" + "Onartu guztiak" + "Bai, ukatu eta ezarri debekua" + "Bai, ukatu" + "Ukatu sarbidea" + "Ukatu eta ezarri debekua" + "Sartzeko eskaerak" + + "%1$s + beste %2$d gelara batu nahi dute" + "%1$s + beste %2$d gelara batu nahi dute" + + "Ikusi guztiak" + "Onartu" + "%1$s(e)k gela honetara sartu nahi du" + "Ikusi" + diff --git a/features/knockrequests/impl/src/main/res/values-nb/translations.xml b/features/knockrequests/impl/src/main/res/values-nb/translations.xml index 33afe86da7..278fdff59e 100644 --- a/features/knockrequests/impl/src/main/res/values-nb/translations.xml +++ b/features/knockrequests/impl/src/main/res/values-nb/translations.xml @@ -1,4 +1,21 @@ + "Ja, godta alle" + "Godta alle forespørsler" + "Godtar alle forespørsler om å bli med" + "Godtar forespørsel om å bli med" + "Avslår og forbyr tilgang" + "Ja, avslå" + "Er du sikker på at du vil avslå %1$ss forespørsel om å bli med i dette rommet?" + "Avslå tilgang" + "Avslå og forby" + "Vi kunne ikke avslå denne forespørselen. Vil du prøve igjen?" + "Kunne ikke avslå forespørselen" + "Avslår forespørsel om å bli med" + "Ingen ventende forespørsel om å bli med" + "Forespørsler om å bli med" + "Vis alle" "Godta" + "%1$s ønsker å bli med i dette rommet" + "Vis" diff --git a/features/leaveroom/api/src/main/res/values-es/translations.xml b/features/leaveroom/api/src/main/res/values-es/translations.xml index 25c753201c..cb393481d8 100644 --- a/features/leaveroom/api/src/main/res/values-es/translations.xml +++ b/features/leaveroom/api/src/main/res/values-es/translations.xml @@ -3,5 +3,5 @@ "¿Estás seguro de que quieres salir de esta conversación? Esta conversación no es pública y no podrás volver a unirte sin una invitación." "¿Estás seguro de que quieres salir de esta sala? Eres la única persona aquí. Si te vas, nadie podrá unirse en el futuro, ni siquiera tú." "¿Estás seguro de que quieres abandonar esta sala? Esta sala no es pública y no podrás volver a entrar sin una invitación." - "¿Seguro que quieres salir de la habitación?" + "¿Seguro que quieres salir de la sala?" diff --git a/features/leaveroom/api/src/main/res/values-eu/translations.xml b/features/leaveroom/api/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..df735861b8 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-eu/translations.xml @@ -0,0 +1,7 @@ + + + "Ziur elkarrizketa utzi nahi duzula? Ez da publikoa eta ezingo zara berriro batu gonbidapenik gabe." + "Ziur gelatik irten nahi duzula? Dagoen pertsona bakarra zara. Ateratzen bazara ezingo da inor batu etorkizunean, ezta zeu ere." + "Ziur gelatik irten nahi duzula? Gela hau ez da publikoa eta ezingo zara berriro batu gonbidapenik gabe." + "Ziur gelatik atera nahi duzula?" + diff --git a/features/leaveroom/api/src/main/res/values-ka/translations.xml b/features/leaveroom/api/src/main/res/values-ka/translations.xml index bb2c4e6ed0..f039365a38 100644 --- a/features/leaveroom/api/src/main/res/values-ka/translations.xml +++ b/features/leaveroom/api/src/main/res/values-ka/translations.xml @@ -1,5 +1,6 @@ + "დარწმუნებული ხართ ამ საუბრის დატოვებაში? იგი საჯარო არაა და მოწვევის გარეშე ხელახლა ვერ შეურთდებით." "დარწმუნებული ბრძანდებით, რომ ამ ოთახის დატოვება გსურთ? თქვენ აქ მარტო ხართ და ჩატის დატოვებისას აქ თქვენს ჩათვლით ვერავინ ვერ გაწევრიანდება." "დარწმუნებული ბრძანდებით, რომ ამ ოთახის დატოვება გსურთ? ეს ოთახი არ არის საჯარო და მოწვევის გარეშე ვერ შეძლებთ ხელახლა გაწევრიანებას." "დარწმუნებული ბრძანდებით, რომ ოთახის დატოვება გსურთ?" diff --git a/features/leaveroom/api/src/main/res/values-nb/translations.xml b/features/leaveroom/api/src/main/res/values-nb/translations.xml index 5b1b4c74c7..1e1800e0d0 100644 --- a/features/leaveroom/api/src/main/res/values-nb/translations.xml +++ b/features/leaveroom/api/src/main/res/values-nb/translations.xml @@ -1,5 +1,6 @@ + "Er du sikker på at du vil forlate denne samtalen? Denne samtalen er ikke offentlig, og du vil ikke kunne bli med igjen uten en invitasjon." "Er du sikker på at du vil forlate dette rommet? Du er den eneste personen her. Hvis du forlater, vil ingen kunne bli med i fremtiden, inkludert deg." "Er du sikker på at du vil forlate dette rommet? Dette rommet er ikke offentlig, og du vil ikke kunne bli med igjen uten en invitasjon." "Er du sikker på at du vil forlate rommet?" diff --git a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt index a567b2d3d7..4943e75185 100644 --- a/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt +++ b/features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenter.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.isDm +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -74,10 +75,11 @@ private suspend fun showLeaveRoomAlert( confirmation: MutableState, ) { matrixClient.getRoom(roomId)?.use { room -> + val roomInfo = room.roomInfoFlow.first() confirmation.value = when { - room.isDm -> Dm(roomId) - !room.isPublic -> PrivateRoom(roomId) - room.joinedMemberCount == 1L -> LastUserInRoom(roomId) + roomInfo.isDm -> Dm(roomId) + !roomInfo.isPublic -> PrivateRoom(roomId) + roomInfo.joinedMembersCount == 1L -> LastUserInRoom(roomId) else -> Generic(roomId) } } diff --git a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterTest.kt b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterTest.kt index e9d229ad93..22407c93d4 100644 --- a/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterTest.kt +++ b/features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/LeaveRoomPresenterTest.kt @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.MatrixClient 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.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -52,7 +53,9 @@ class LeaveRoomPresenterTest { client = FakeMatrixClient().apply { givenGetRoomResult( roomId = A_ROOM_ID, - result = FakeMatrixRoom() + result = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(isDirect = false, isPublic = true, joinedMembersCount = 10)) + } ) } ) @@ -72,7 +75,9 @@ class LeaveRoomPresenterTest { client = FakeMatrixClient().apply { givenGetRoomResult( roomId = A_ROOM_ID, - result = FakeMatrixRoom(isPublic = false), + result = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(isPublic = false)) + }, ) } ) @@ -92,7 +97,9 @@ class LeaveRoomPresenterTest { client = FakeMatrixClient().apply { givenGetRoomResult( roomId = A_ROOM_ID, - result = FakeMatrixRoom(joinedMemberCount = 1), + result = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(joinedMembersCount = 1)) + }, ) } ) @@ -112,7 +119,9 @@ class LeaveRoomPresenterTest { client = FakeMatrixClient().apply { givenGetRoomResult( roomId = A_ROOM_ID, - result = FakeMatrixRoom(activeMemberCount = 2, isDirect = true), + result = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(isDirect = true, activeMembersCount = 2)) + }, ) } ) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt index 1be54e7b43..dd273ebac1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/activity/PinUnlockActivity.kt @@ -22,6 +22,7 @@ import io.element.android.features.lockscreen.impl.unlock.PinUnlockPresenter import io.element.android.features.lockscreen.impl.unlock.PinUnlockView import io.element.android.features.lockscreen.impl.unlock.di.PinUnlockBindings import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.coroutines.launch @@ -38,6 +39,7 @@ class PinUnlockActivity : AppCompatActivity() { @Inject lateinit var lockScreenService: LockScreenService @Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var enterpriseService: EnterpriseService + @Inject lateinit var buildMeta: BuildMeta override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() @@ -47,6 +49,7 @@ class PinUnlockActivity : AppCompatActivity() { ElementThemeApp( appPreferencesStore = appPreferencesStore, enterpriseService = enterpriseService, + buildMeta = buildMeta, ) { val state = presenter.present() PinUnlockView( diff --git a/features/lockscreen/impl/src/main/res/values-es/translations.xml b/features/lockscreen/impl/src/main/res/values-es/translations.xml index 1ab64c8f3b..c0e34a3763 100644 --- a/features/lockscreen/impl/src/main/res/values-es/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-es/translations.xml @@ -3,6 +3,7 @@ "autenticación biométrica" "desbloqueo biométrico" "Desbloquear con biométrico" + "Confirmar datos biométricos" "¿Olvidaste el PIN?" "Cambiar código PIN" "Permitir desbloqueo biométrico" diff --git a/features/lockscreen/impl/src/main/res/values-eu/translations.xml b/features/lockscreen/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..74c5e79ffd --- /dev/null +++ b/features/lockscreen/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,33 @@ + + + "autentifikazio biometrikoa" + "desblokeo biometrikoa" + "Desblokeatu biometria bidez" + "PINa ahaztu duzu?" + "Aldatu PIN kodea" + "Baimendu desblokeo biometrikoa" + "Kendu PIN kodea" + "Ziur PINa kendu nahi duzula?" + "PINa kendu?" + "Baimendu %1$s" + "Nahiago dut PINa erabili" + "Aukeratu PINa" + "Berretsi PINa" + "Segurtasun arrazoiak direla eta, ezin duzu hau aukeratu PIN kode gisa" + "Aukeratu PIN ezberdin bat" + "Sartu birritan PIN bera" + "PINak ez datoz bat" + "Saioa berriro hasi eta PIN berri bat sortu beharko duzu aurrera jarraitzeko" + "Saioa amaitzen ari zara" + + "Saiakera %1$d duzu desblokeatzeko" + "%1$d saiakera dituzu desblokeatzeko" + + + "PIN okerra. Aukera %1$d gehiago duzu" + "Pin okerra. %1$d aukera gehiago dituzu." + + "Erabili biometria" + "Erabili PINa" + "Saioa amaitzen…" + diff --git a/features/lockscreen/impl/src/main/res/values-nb/translations.xml b/features/lockscreen/impl/src/main/res/values-nb/translations.xml index cd619e09d4..89ca8c0eaf 100644 --- a/features/lockscreen/impl/src/main/res/values-nb/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-nb/translations.xml @@ -1,7 +1,38 @@ + "biometrisk autentisering" + "biometrisk opplåsing" + "Lås opp med biometri" + "Bekreft biometri" + "Glemt PIN-koden?" + "Endre PIN-kode" + "Tillat biometrisk opplåsing" + "Fjern PIN-kode" + "Er du sikker på at du vil fjerne PIN-koden?" + "Fjerne PIN-kode?" + "Tillat %1$s" + "Jeg vil heller bruke PIN-kode" + "Spar deg selv litt tid og bruk%1$s for å låse opp appen hver gang" + "Velg PIN-kode" + "Bekreft PIN-kode" "Lås %1$s for å legge til ekstra sikkerhet til chattene dine. Velg noe som du husker. Hvis du glemmer denne PIN-koden, blir du logget ut av appen." + "Du kan ikke velge dette som PIN-kode av sikkerhetsmessige årsaker" + "Velg en annen PIN-kode" + "Skriv inn samme PIN-kode to ganger" + "PIN-kodene samsvarer ikke" + "Du må logge inn på nytt og opprette en ny PIN-kode for å fortsette" + "Du blir logget av" + + "Du har %1$d forsøk på å låse opp" + "Du har %1$d forsøk på å låse opp" + + + "Feil PIN-kode. Du har %1$d forsøk igjen" + "Feil PIN-kode. Du har %1$d forsøk igjen" + + "Bruk biometri" + "Bruk PIN-kode" "Logger ut…" diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt index e8b931ffe9..091f2e6e02 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderView.kt @@ -83,9 +83,9 @@ fun AccountProviderView( modifier = Modifier .padding(start = 10.dp) .size(16.dp), - resourceId = R.drawable.ic_public, + imageVector = CompoundIcons.Public(), contentDescription = null, - tint = Color.Unspecified, + tint = ElementTheme.colors.iconSecondary, ) } } 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 359e556e69..eb5a8fee67 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 @@ -93,7 +93,7 @@ class ConfirmAccountProviderPresenter @AssistedInject constructor( val matrixHomeServerDetails = authenticationService.getHomeserverDetails().value!! if (matrixHomeServerDetails.supportsOidcLogin) { // Retrieve the details right now - val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Consent + val oidcPrompt = if (params.isAccountCreation) OidcPrompt.Create else OidcPrompt.Login LoginFlow.OidcFlow(authenticationService.getOidcUrl(oidcPrompt).getOrThrow()) } else if (params.isAccountCreation) { val url = webClientUrlForAuthenticationRetriever.retrieve(homeserverUrl) diff --git a/features/login/impl/src/main/res/drawable/ic_public.xml b/features/login/impl/src/main/res/drawable/ic_public.xml deleted file mode 100644 index fc1eacbc9f..0000000000 --- a/features/login/impl/src/main/res/drawable/ic_public.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/features/login/impl/src/main/res/values-es/translations.xml b/features/login/impl/src/main/res/values-es/translations.xml index 68851e411c..a6df64575a 100644 --- a/features/login/impl/src/main/res/values-es/translations.xml +++ b/features/login/impl/src/main/res/values-es/translations.xml @@ -31,7 +31,49 @@ "Matrix es una red abierta para una comunicación segura y descentralizada." "¡Hola de nuevo!" "Iniciar sesión en %1$s" - "Inténtalo de nuevo" + "Estableciendo una conexión segura" + "No se pudo establecer una conexión segura con el nuevo dispositivo. Tus dispositivos actuales siguen siendo seguros y no tienes que preocuparte por ellos." + "¿Y ahora qué?" + "Intenta iniciar sesión de nuevo con un código QR en caso de que se trate de un problema de red" + "Si te encuentras con el mismo problema, prueba con una red wifi diferente o usa tus datos móviles en lugar de wifi" + "Si eso no funciona, inicia sesión manualmente" + "La conexión no es segura" + "Se te pedirá que introduzcas los dos dígitos mostrados en este dispositivo." + "Introduce el número que aparece a continuación en tu otro dispositivo" + "Inicia sesión en tu otro dispositivo e inténtalo de nuevo, o usa otro dispositivo en el que ya hayas iniciado sesión." + "El otro dispositivo no tiene iniciada la sesión" + "El inicio de sesión se canceló en el otro dispositivo." + "Solicitud de inicio de sesión cancelada" + "El inicio de sesión se rechazó en el otro dispositivo." + "Inicio de sesión rechazado" + "El inicio de sesión ha caducado. Inténtalo de nuevo." + "El inicio de sesión no se completó a tiempo" + "Tu otro dispositivo no admite el inicio de sesión en %s con un código QR. + +Intenta iniciar sesión manualmente o escanea el código QR con otro dispositivo." + "Código QR no admitido" + "Tu proveedor de cuenta no es compatible con %1$s." + "%1$s no admitido" + "Listo para escanear" + "Abre %1$s en un dispositivo de escritorio" + "Haz clic en tu avatar" + "Selecciona %1$s" + "«Vincular un dispositivo nuevo»" + "Escanea el código QR con este dispositivo" + "Solo disponible si tu proveedor de cuenta lo admite." + "Abre %1$s en otro dispositivo para obtener el código QR" + "Usa el código QR que se muestra en el otro dispositivo." + "Intentar de nuevo" + "Código QR incorrecto" + "Ir a los ajustes de la cámara" + "Tienes que dar permiso a %1$s para que utilice la cámara de tu dispositivo y así poder continuar." + "Permite el acceso a la cámara para escanear el código QR" + "Escanea el código QR" + "Empezar de nuevo" + "Se ha producido un error inesperado. Vuelve a intentarlo." + "A la espera de tu otro dispositivo" + "Puede que el proveedor de tu cuenta te pida el siguiente código para verificar el inicio de sesión." + "Tu código de verificación" "Cambiar el proveedor de la cuenta" "Un servidor privado para los empleados de Element." "Matrix es una red abierta para una comunicación segura y descentralizada." diff --git a/features/login/impl/src/main/res/values-eu/translations.xml b/features/login/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..2abc6c823e --- /dev/null +++ b/features/login/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,63 @@ + + + "Aldatu kontu-hornitzailea" + "Zerbitzariaren helbidea" + "Sartu bilaketa-kontsulta edo domeinu-helbidea." + "Bilatu enpresa, komunitate edo zerbitzari pribatu bat." + "Bilatu kontu-hornitzaile bat" + "%s(e)n saioa hastear zaude" + "%s(e)n kontua sortzear zaude" + "Beste bat" + "Erabili beste kontu-hornitzaile bat, hala nola zure zerbitzari pribatua edo laneko kontu bat." + "Aldatu kontu-hornitzailea" + "Zerbitzari hau ez da gaur-gaurkoz sliding sync-ekin bateragarria." + "Zerbitzariaren URLa" + "Zein da zure zerbitzariaren helbidea?" + "Hautatu zure zerbitzaria" + "Sortu kontua" + "Kontu hau desaktibatuta dago." + "Erabiltzaile-izena edo/eta pasahitza okerrak" + "Sartu zure datuak" + "Matrix komunikazio seguru eta deszentralizaturako sare irekia da." + "Ongi etorri!" + "Hasi saioa %1$s(e)n" + "Konexio segurua ezartzen" + "Ezin izan da konexio segururik ezarri gailu berriarekin. Lehendik dauden gailuak seguru daude oraindik ere eta ez duzu haietaz kezkatu beharrik." + "Orain zer?" + "Horrek ez badu funtzionatzen, hasi saioa eskuz" + "Konexioa ez da segurua" + "Gailu honetan agertzen diren bi digituak sartzeko eskatuko zaizu." + "Sartu beheko zenbakia beste gailuan" + "Hasi saioa beste gailu batean eta saiatu berriro, edo erabili saioa hasita duen beste gailuren bat." + "Saioa hasteko eskaera bertan behera utzi da beste gailuan" + "Saioa hasteko eskaera bertan behera utzi da" + "Saio-hasiera ukatu da" + "Saio-hasiera iraungi da. Saiatu berriro." + "Saio-hasiera ez da garaiz gauzatu." + "Beste gailua ez da bateragarria QR kodeak erabiliz %s(e)n saioa hastearekin. + +Saiatu saioa eskuz hasten, edo eskaneatu QR kodea beste gailu batean." + "QR kodea ez da bateragarria" + "Zure kontu-hornitzailea ez da %1$s-ekin bateragarria." + "%1$s ez da bateragarria" + "Eskaneatzeko prest" + "Klikatu zure abatarrean" + "Hautatu %1$s" + "\"Lotu gailu berria\"" + "Eskaneatu QR kodea gailu honekin" + "Erabili beste gailuan agertzen den QR kodea." + "Saiatu berriro" + "QR kode okerra" + "Joan kameraren ezarpenetara" + "Eskaneatu QR kodea" + "Hasi berriro" + "Ustekabeko errore bat gertatu da. Saiatu berriro." + "Beste gailuaren zain" + "Zure kontu-hornitzaileak hurrengo kodea eska diezazuke saio-hasiera egiaztatzeko." + "Egiaztapen-kodea" + "Aldatu kontu-hornitzailea" + "Elementeko langileentzako zerbitzari pribatua." + "Matrix komunikazio seguru eta deszentralizaturako sare irekia da." + "%1$s(e)n saioa hastear zaude" + "%1$s(e)n kontua sortzear zaude" + 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 0b5c5ec6b5..b7cddcf7e7 100644 --- a/features/login/impl/src/main/res/values-ka/translations.xml +++ b/features/login/impl/src/main/res/values-ka/translations.xml @@ -14,6 +14,7 @@ "გამოიყენეთ სხვა ანგარიშის პროვაიდერი, როგორიცაა თქვენი პირადი სერვერი ან სამუშაო ანგარიში." "შეცვალეთ ანგარიშის მომწოდებელი" "ჩვენ ვერ მივაღწიეთ ამ სახლის სერვერს. გთხოვთ, შეამოწმოთ, რომ სწორად შეიყვანეთ სახლის სერვერის URL. თუ URL სწორია, დაუკავშირდით თქვენი სახლის სერვერის ადმინისტრატორს დამატებითი დახმარებისთვის." + "Sliding sync არ არის ხელმისაწვდომი well-known ფაილში პრობლემის გამო: %1$s" "ამჟამად ეს სერვერი მხარს არ უჭერს \"sliding sync\"-ს." "სახლის სერვერის URL" "თქვენ შეგიძლიათ დაუკავშირდეთ მხოლოდ იმ სერვერს, რომელიც მხარს უჭერს \"sliding sync\"-ს. თქვენი სახლის სერვერის ადმინისტრატორს დასჭირდება მისი კონფიგურაცია.%1$s" @@ -23,6 +24,7 @@ "ეს ანგარიში დეაქტივირებულია." "არასწორი მომხმარებლის სახელი და/ან პაროლი" "მოცემული მომხმარებლის იდენტიფიკატორი არასწორია. დასაშვები ფორმატი: ‘@user:homeserver.org’" + "ეს სერვერი კონფიგურირებულია განახლების გასაღებების გამოსაყენებლად. პაროლზე დაფუძნებული შეცვლისას ისინი მხარდაჭერილი არაა." "მოცემული სახლის სერვერი მხარს არ უჭერს პაროლით ან OIDC-ით შესვლას. გთხოვთ, დაუკავშირდეთ თქვენს ადმინისტრატორს ან აარჩიეთ სხვა სახლის სერვერი." "შეიყვანეთ თქვენი დეტალები" "Matrix არის ღია ქსელი უსაფრთხო, დეცენტრალიზებული კომუნიკაციისთვის." diff --git a/features/login/impl/src/main/res/values-nb/translations.xml b/features/login/impl/src/main/res/values-nb/translations.xml index 5f4ab4ee08..3accfb170e 100644 --- a/features/login/impl/src/main/res/values-nb/translations.xml +++ b/features/login/impl/src/main/res/values-nb/translations.xml @@ -14,6 +14,8 @@ "Bruk en annen kontotilbyder, for eksempel din egen private server eller en arbeidskonto." "Bytt kontotilbyder" "Vi kunne ikke nå denne hjemmeserveren. Kontroller at du har skrevet inn hjemmeserverens URL riktig. Hvis URL-en er riktig, kontakt administratoren for hjemmeserveren din for å få mer hjelp." + "Glidende synkronisering er ikke tilgjengelig på grunn av et problem i den velkjente filen: +%1$s" "Denne serveren støtter for øyeblikket ikke sliding sync." "URL til hjemmeserver" "Du kan bare koble til en eksisterende server som støtter sliding sync. Administrator for din hjemmeserver må konfigurere det. %1$s" @@ -23,11 +25,54 @@ "Denne kontoen er deaktivert." "Feil brukernavn og/eller passord" "Dette er ikke en gyldig brukeridentifikator. Forventet format: \'@bruker:homeserver.org\'" + "Denne serveren er konfigurert til å bruke oppdateringstokener. Disse støttes ikke når du bruker passordbasert pålogging." "Den valgte hjemmeserveren støtter ikke passord eller OIDC-pålogging. Ta kontakt med administratoren din eller velg en annen hjemmeserver." "Skriv inn opplysningene dine" "Matrix er et åpent nettverk for sikker, desentralisert kommunikasjon." "Velkommen tilbake!" "Logg inn på %1$s" + "Etablere en sikker forbindelse" + "En sikker tilkobling kunne ikke opprettes til den nye enheten. Dine eksisterende enheter er fortsatt trygge, og du trenger ikke å bekymre deg for dem." + "Hva nå?" + "Prøv å logge på igjen med en QR-kode i tilfelle dette var et nettverksproblem" + "Hvis du støter på det samme problemet, kan du prøve et annet wifi-nettverk eller bruke mobildata i stedet for wifi" + "Hvis det ikke fungerer, kan du logge på manuelt" + "Forbindelsen er ikke sikker" + "Du blir bedt om å skrive inn de to sifrene som vises på denne enheten." + "Skriv inn nummeret nedenfor på den andre enheten" + "Logg på den andre enheten din og prøv igjen, eller bruk en annen enhet som allerede er pålogget." + "Annen enhet er ikke pålogget" + "Påloggingen ble kansellert på den andre enheten." + "Påloggingsforespørsel kansellert" + "Påloggingen ble avvist på den andre enheten." + "Pålogging avslått" + "Påloggingen er utløpt. Vennligst prøv igjen." + "Påloggingen ble ikke fullført i tide" + "Den andre enheten din støtter ikke pålogging på %s med en QR-kode. + +Prøv å logge på manuelt, eller skann QR-koden med en annen enhet." + "QR-kode støttes ikke" + "Kontotilbyderen din støtter ikke %1$s." + "%1$s støttes ikke" + "Klar til å skanne" + "Klikk på avataren din" + "Velg %1$s" + "«Koble til ny enhet»" + "Skann QR-koden med denne enheten" + "Bare tilgjengelig hvis kontotilbyderen din støtter det." + "Åpne %1$s på en annen enhet for å få QR-koden" + "Bruk QR-koden som vises på den andre enheten." + "Prøv igjen" + "Feil QR-kode" + "Gå til kamerainnstillinger" + "Du må gi tillatelse til at %1$s kan bruke enhetens kamera for å fortsette." + "Tillat kameratilgang for å skanne QR-koden" + "Skann QR-koden" + "Begynn på nytt" + "Det oppstod en uventet feil. Prøv igjen." + "Venter på den andre enheten din" + "Kontotilbyderen din kan be om følgende kode for å bekrefte påloggingen." + "Bekreftelseskoden din" "Bytt kontotilbyder" "En privat server for Element-ansatte." "Matrix er et åpent nettverk for sikker, desentralisert kommunikasjon." diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt index 7249871772..4b8e23ab4e 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/LogoutActionDialog.kt @@ -8,7 +8,6 @@ package io.element.android.features.logout.impl.ui import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.res.stringResource import io.element.android.features.logout.impl.R import io.element.android.libraries.architecture.AsyncAction diff --git a/features/logout/impl/src/main/res/values-eu/translations.xml b/features/logout/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..9d25ef2ba6 --- /dev/null +++ b/features/logout/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,12 @@ + + + "Ziur saioa amaitu nahi duzula?" + "Amaitu saioa" + "Amaitu saioa" + "Saioa amaitzen…" + "Babeskopia desaktibatu duzu" + "Itxaron eragiketa amaitu arte saioa amaitu baino lehen." + "Amaitu saioa" + "Berreskuratzea ez da konfiguratu" + "Gorde al duzu berreskuratze-gakoa?" + diff --git a/features/logout/impl/src/main/res/values-nb/translations.xml b/features/logout/impl/src/main/res/values-nb/translations.xml index 4e02108299..0e4a735239 100644 --- a/features/logout/impl/src/main/res/values-nb/translations.xml +++ b/features/logout/impl/src/main/res/values-nb/translations.xml @@ -4,5 +4,15 @@ "Logg ut" "Logg ut" "Logger ut…" + "Du er i ferd med å logge av din siste sesjon. Hvis du logger av nå, mister du tilgangen til de krypterte meldingene dine." + "Du har slått av sikkerhetskopiering" + "Nøklene dine ble fortsatt sikkerhetskopiert da du koblet fra. Koble til igjen, slik at nøklene dine kan sikkerhetskopieres før du logger av." + "Nøklene dine blir fortsatt sikkerhetskopiert" + "Vent til dette er fullført før du logger av." + "Nøklene dine blir fortsatt sikkerhetskopiert" "Logg ut" + "Du er i ferd med å logge ut av din siste sesjon. Hvis du logger ut nå, mister du tilgangen til de krypterte meldingene dine." + "Gjenoppretting ikke konfigurert" + "Du er i ferd med å logge av din siste sesjon. Hvis du logger av nå, kan du miste tilgangen til de krypterte meldingene dine." + "Har du lagret gjenopprettingsnøkkelen din?" 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 2a84150540..f2befcb7c6 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 @@ -45,7 +45,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStickerContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.duration @@ -379,19 +378,6 @@ class MessagesFlowNode @AssistedInject constructor( thumbnailSource = event.content.thumbnailSource, ) } - is TimelineItemStickerContent -> { - /* Sticker may have an empty url and no thumbnail - if encrypted on certain bridges */ - event.content.preferredMediaSource?.let { preferredMediaSource -> - buildMediaViewerNavTarget( - mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(timelineMode), - event = event, - content = event.content, - mediaSource = preferredMediaSource, - thumbnailSource = event.content.thumbnailSource, - ) - } - } is TimelineItemVideoContent -> { buildMediaViewerNavTarget( mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(timelineMode), 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 a8076d023c..4ea6a1c9d3 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 @@ -59,11 +59,14 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch @ContributesNode(RoomScope::class) class MessagesNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val coroutineScope: CoroutineScope, private val room: MatrixRoom, private val analyticsService: AnalyticsService, messageComposerPresenterFactory: MessageComposerPresenter.Factory, @@ -108,7 +111,7 @@ class MessagesNode @AssistedInject constructor( super.onBuilt() lifecycle.subscribe( onCreate = { - analyticsService.capture(room.toAnalyticsViewRoom()) + coroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) } }, onDestroy = { mediaPlayer.close() 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 6b1f50c41d..0ace1a9f47 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 @@ -21,6 +21,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.lifecycle.compose.LifecycleResumeEffect import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -33,7 +34,6 @@ import io.element.android.features.messages.impl.actionlist.model.TimelineItemAc import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerState -import io.element.android.features.messages.impl.messagecomposer.observeRoomMemberIdentityStateChange import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineEvents @@ -61,6 +61,8 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage 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.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.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo @@ -76,10 +78,10 @@ import io.element.android.libraries.matrix.api.sync.isOnline import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.matrix.ui.room.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.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -110,6 +112,7 @@ class MessagesPresenter @AssistedInject constructor( private val timelineController: TimelineController, private val permalinkParser: PermalinkParser, private val analyticsService: AnalyticsService, + private val encryptionService: EncryptionService, ) : Presenter { @AssistedFactory interface Factory { @@ -125,7 +128,7 @@ class MessagesPresenter @AssistedInject constructor( override fun present(): MessagesState { htmlConverterProvider.Update(currentUserId = room.sessionId) - val roomInfo by room.roomInfoFlow.collectAsState(null) + val roomInfo by room.roomInfoFlow.collectAsState() val localCoroutineScope = rememberCoroutineScope() val composerState = composerPresenter.present() val voiceMessageComposerState = voiceMessageComposerPresenter.present() @@ -144,34 +147,37 @@ class MessagesPresenter @AssistedInject constructor( val userEventPermissions by userEventPermissions(syncUpdateFlow.value) val roomName: AsyncData by remember { - derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized } + derivedStateOf { roomInfo.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized } } val roomAvatar: AsyncData by remember { - derivedStateOf { roomInfo?.avatarData()?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized } + derivedStateOf { AsyncData.Success(roomInfo.avatarData()) } } val heroes by remember { - derivedStateOf { roomInfo?.heroes().orEmpty().toPersistentList() } + derivedStateOf { roomInfo.heroes().toPersistentList() } } var hasDismissedInviteDialog by rememberSaveable { mutableStateOf(false) } - val roomMemberIdentityStateChanges by produceState(persistentListOf()) { - observeRoomMemberIdentityStateChange(room) - } LaunchedEffect(Unit) { // Remove the unread flag on entering but don't send read receipts // as those will be handled by the timeline. withContext(dispatchers.io) { room.setUnreadFlag(isUnread = false) + + // If for some reason the encryption state is unknown, fetch it + if (roomInfo.isEncrypted == null) { + room.getUpdatedIsEncrypted() + } } } val inviteProgress = remember { mutableStateOf>(AsyncData.Uninitialized) } var showReinvitePrompt by remember { mutableStateOf(false) } - LaunchedEffect(hasDismissedInviteDialog, composerState.textEditorState.hasFocus(), syncUpdateFlow.value) { + val composerHasFocus by remember { derivedStateOf { composerState.textEditorState.hasFocus() } } + LaunchedEffect(hasDismissedInviteDialog, composerHasFocus, roomInfo) { withContext(dispatchers.io) { - showReinvitePrompt = !hasDismissedInviteDialog && composerState.textEditorState.hasFocus() && room.isDm && room.activeMemberCount == 1L + showReinvitePrompt = !hasDismissedInviteDialog && composerHasFocus && roomInfo.isDm && roomInfo.activeMembersCount == 1L } } val isOnline by syncService.isOnline().collectAsState() @@ -183,6 +189,25 @@ class MessagesPresenter @AssistedInject constructor( enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages) } + var dmUserVerificationState by remember { mutableStateOf(null) } + + val membersState by room.membersStateFlow.collectAsState() + val dmRoomMember by room.getDirectRoomMember(membersState) + val roomMemberIdentityStateChanges = identityChangeState.roomMemberIdentityStateChanges + + LifecycleResumeEffect(dmRoomMember, roomInfo.isEncrypted) { + if (roomInfo.isEncrypted == true) { + val dmRoomMemberId = dmRoomMember?.userId + localCoroutineScope.launch { + dmRoomMemberId?.let { userId -> + dmUserVerificationState = roomMemberIdentityStateChanges.find { it.identityRoomMember.userId == userId }?.identityState + ?: encryptionService.getUserIdentity(userId).getOrNull() + } + } + } + onPauseOrDispose {} + } + fun handleEvents(event: MessagesEvents) { when (event) { is MessagesEvents.HandleAction -> { @@ -215,7 +240,6 @@ class MessagesPresenter @AssistedInject constructor( roomAvatar = roomAvatar, heroes = heroes, composerState = composerState, - roomMemberIdentityStateChanges = roomMemberIdentityStateChanges, userEventPermissions = userEventPermissions, voiceMessageComposerState = voiceMessageComposerState, timelineState = timelineState, @@ -234,6 +258,7 @@ class MessagesPresenter @AssistedInject constructor( appName = buildMeta.applicationName, roomCallState = roomCallState, pinnedMessagesBannerState = pinnedMessagesBannerState, + dmUserVerificationState = dmUserVerificationState, eventSink = { handleEvents(it) } ) } @@ -255,7 +280,7 @@ class MessagesPresenter @AssistedInject constructor( return AvatarData( id = id.value, name = name, - url = avatarUrl ?: room.avatarUrl, + url = avatarUrl ?: room.info().avatarUrl, size = AvatarSize.TimelineRoom ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 61f092eaf9..4e6fe3cc94 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -10,7 +10,6 @@ package io.element.android.features.messages.impl import androidx.compose.runtime.Immutable import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState -import io.element.android.features.messages.impl.crypto.identity.RoomMemberIdentityStateChange import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.TimelineState @@ -24,6 +23,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import kotlinx.collections.immutable.ImmutableList @Immutable @@ -34,7 +34,6 @@ data class MessagesState( val heroes: ImmutableList, val userEventPermissions: UserEventPermissions, val composerState: MessageComposerState, - val roomMemberIdentityStateChanges: ImmutableList, val voiceMessageComposerState: VoiceMessageComposerState, val timelineState: TimelineState, val timelineProtectionState: TimelineProtectionState, @@ -52,5 +51,6 @@ data class MessagesState( val roomCallState: RoomCallState, val appName: String, val pinnedMessagesBannerState: PinnedMessagesBannerState, + val dmUserVerificationState: IdentityState?, val eventSink: (MessagesEvents) -> Unit ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index a73a4c1565..b5b87b2f87 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 @@ -11,7 +11,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.crypto.identity.IdentityChangeState -import io.element.android.features.messages.impl.crypto.identity.RoomMemberIdentityStateChange import io.element.android.features.messages.impl.crypto.identity.anIdentityChangeState import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.messagecomposer.aMessageComposerState @@ -40,9 +39,9 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState 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 @@ -82,6 +81,8 @@ open class MessagesStateProvider : PreviewParameterProvider { currentPinnedMessageIndex = 0, ), ), + aMessagesState(roomName = AsyncData.Success("A DM with a very looong name"), dmUserVerificationState = IdentityState.Verified), + aMessagesState(roomName = AsyncData.Success("A DM with a very looong name"), dmUserVerificationState = IdentityState.VerificationViolation), ) } @@ -94,7 +95,6 @@ fun aMessagesState( isFullScreen = false, mode = MessageComposerMode.Normal, ), - roomMemberIdentityStateChanges: ImmutableList = persistentListOf(), voiceMessageComposerState: VoiceMessageComposerState = aVoiceMessageComposerState(), timelineState: TimelineState = aTimelineState( timelineItems = aTimelineItemList(aTimelineItemTextContent()), @@ -112,6 +112,7 @@ fun aMessagesState( enableVoiceMessages: Boolean = true, roomCallState: RoomCallState = aStandByCallState(), pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(), + dmUserVerificationState: IdentityState? = null, eventSink: (MessagesEvents) -> Unit = {}, ) = MessagesState( roomId = RoomId("!id:domain"), @@ -120,7 +121,6 @@ fun aMessagesState( heroes = persistentListOf(), userEventPermissions = userEventPermissions, composerState = composerState, - roomMemberIdentityStateChanges = roomMemberIdentityStateChanges, voiceMessageComposerState = voiceMessageComposerState, timelineProtectionState = timelineProtectionState, identityChangeState = identityChangeState, @@ -138,6 +138,7 @@ fun aMessagesState( roomCallState = roomCallState, appName = "Element", pinnedMessagesBannerState = pinnedMessagesBannerState, + dmUserVerificationState = dmUserVerificationState, eventSink = eventSink, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index a7d312b816..5d6ad6fcbe 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidthIn import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape @@ -50,6 +51,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction @@ -88,6 +90,7 @@ import io.element.android.libraries.designsystem.components.dialogs.Confirmation import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.BottomSheetDragHandle +import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -188,6 +191,7 @@ fun MessagesView( roomAvatar = state.roomAvatar.dataOrNull(), heroes = state.heroes, roomCallState = state.roomCallState, + dmUserIdentityState = state.dmUserVerificationState, onBackClick = { hidingKeyboard { onBackClick() } }, onRoomDetailsClick = { hidingKeyboard { onRoomDetailsClick() } }, onJoinCallClick = onJoinCallClick, @@ -427,7 +431,7 @@ private fun MessagesViewComposerBottomSheetContents( onLinkClick = onLinkClick, ) } - val verificationViolation = state.roomMemberIdentityStateChanges.firstOrNull { + val verificationViolation = state.identityChangeState.roomMemberIdentityStateChanges.firstOrNull { it.identityState == IdentityState.VerificationViolation } if (verificationViolation != null) { @@ -454,6 +458,7 @@ private fun MessagesViewTopBar( roomAvatar: AvatarData?, heroes: ImmutableList, roomCallState: RoomCallState, + dmUserIdentityState: IdentityState?, onRoomDetailsClick: () -> Unit, onJoinCallClick: () -> Unit, onBackClick: () -> Unit, @@ -463,22 +468,47 @@ private fun MessagesViewTopBar( BackButton(onClick = onBackClick) }, title = { - val roundedCornerShape = RoundedCornerShape(8.dp) - val titleModifier = Modifier - .clip(roundedCornerShape) - .clickable { onRoomDetailsClick() } - if (roomName != null && roomAvatar != null) { - RoomAvatarAndNameRow( - roomName = roomName, - roomAvatar = roomAvatar, - heroes = heroes, - modifier = titleModifier - ) - } else { - IconTitlePlaceholdersRowMolecule( - iconSize = AvatarSize.TimelineRoom.dp, - modifier = titleModifier - ) + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + val roundedCornerShape = RoundedCornerShape(8.dp) + val titleModifier = Modifier + .clip(roundedCornerShape) + .clickable { onRoomDetailsClick() } + if (roomName != null && roomAvatar != null) { + RoomAvatarAndNameRow( + roomName = roomName, + roomAvatar = roomAvatar, + heroes = heroes, + modifier = titleModifier + ) + } else { + IconTitlePlaceholdersRowMolecule( + iconSize = AvatarSize.TimelineRoom.dp, + modifier = titleModifier + ) + } + + when (dmUserIdentityState) { + IdentityState.Verified -> { + Icon( + modifier = Modifier.requiredWidthIn(min = 16.dp), + imageVector = CompoundIcons.Verified(), + tint = ElementTheme.colors.iconSuccessPrimary, + contentDescription = null, + ) + } + IdentityState.VerificationViolation -> { + Icon( + modifier = Modifier.requiredWidthIn(min = 16.dp), + imageVector = CompoundIcons.ErrorSolid(), + tint = ElementTheme.colors.iconCriticalPrimary, + contentDescription = null, + ) + } + else -> Unit + } } }, actions = { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeState.kt index 627c304bad..0ff989fe70 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeState.kt @@ -7,15 +7,10 @@ package io.element.android.features.messages.impl.crypto.identity -import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import kotlinx.collections.immutable.ImmutableList data class IdentityChangeState( val roomMemberIdentityStateChanges: ImmutableList, val eventSink: (IdentityChangeEvent) -> Unit, ) - -data class RoomMemberIdentityStateChange( - val identityRoomMember: IdentityRoomMember, - val identityState: IdentityState, -) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt index 4aba5dc440..4e37f34559 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenter.kt @@ -11,11 +11,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.rememberCoroutineScope -import io.element.android.features.messages.impl.messagecomposer.observeRoomMemberIdentityStateChange import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.ui.room.observeRoomMemberIdentityStateChange import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt index f64f505f0f..858231ebd5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateProvider.kt @@ -12,6 +12,8 @@ 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.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.ui.room.IdentityRoomMember +import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import kotlinx.collections.immutable.toImmutableList class IdentityChangeStateProvider : PreviewParameterProvider { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt index 8362515b6a..464cb59592 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateView.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.encryption.identity.isAViolation +import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt deleted file mode 100644 index 536a1072a7..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityRoomMember.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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.messages.impl.crypto.identity - -import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.api.core.UserId - -data class IdentityRoomMember( - val userId: UserId, - val displayNameOrDefault: String, - val avatarData: AvatarData, -) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/DisabledComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/DisabledComposerView.kt index 19e1758be8..e72e094a5e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/DisabledComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/DisabledComposerView.kt @@ -25,7 +25,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons @@ -33,31 +32,24 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.textcomposer.R +import io.element.android.libraries.designsystem.theme.components.IconColorButton +import io.element.android.libraries.designsystem.theme.components.IconColorButtonStyle @Composable internal fun DisabledComposerView( modifier: Modifier = Modifier, ) { Row( - modifier = modifier.padding(3.dp) + modifier = modifier + .padding(3.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { - IconButton( - modifier = Modifier - .size(48.dp), - enabled = false, + IconColorButton( onClick = {}, - ) { - Icon( - modifier = Modifier.size(30.dp), - resourceId = CommonDrawables.ic_plus_composer, - contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment), - tint = ElementTheme.colors.iconDisabled, - ) - } + imageVector = CompoundIcons.Plus(), + iconColorButtonStyle = IconColorButtonStyle.Disabled, + ) val bgColor = ElementTheme.colors.bgCanvasDisabled val borderColor = ElementTheme.colors.borderDisabled 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 f3a44559bc..fce8e732e4 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 @@ -202,7 +202,7 @@ class MessageComposerPresenter @AssistedInject constructor( suspend fun canSendRoomMention(): Boolean { val userCanSendAtRoom = room.canUserTriggerRoomNotification(currentUserId).getOrDefault(false) - return !room.isDm && userCanSendAtRoom + return !room.isDm() && userCanSendAtRoom } // This will trigger a search immediately when `@` is typed 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 201ec01954..c3e936c549 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 @@ -8,10 +8,12 @@ package io.element.android.features.messages.impl.pinned.list import android.content.Context +import android.view.HapticFeedbackConstants import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -23,6 +25,7 @@ import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @@ -30,6 +33,7 @@ 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.permalink.PermalinkParser import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(RoomScope::class) class PinnedMessagesListNode @AssistedInject constructor( @@ -98,6 +102,7 @@ class PinnedMessagesListNode @AssistedInject constructor( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { val context = LocalContext.current + val view = LocalView.current val state = presenter.present() PinnedMessagesListView( state = state, @@ -105,6 +110,15 @@ class PinnedMessagesListNode @AssistedInject constructor( onEventClick = ::onEventClick, onUserDataClick = ::onUserDataClick, onLinkClick = { url -> onLinkClick(context, url) }, + onLinkLongClick = { + view.performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS + ) + context.copyToClipboard( + it, + context.getString(CommonStrings.common_copied_to_clipboard) + ) + }, modifier = modifier ) } 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 d00e9e7d1a..9eed64ca2d 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 @@ -38,11 +38,11 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.roomMembers +import io.element.android.libraries.matrix.ui.room.isDmAsState 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 @@ -85,10 +85,12 @@ class PinnedMessagesListPresenter @AssistedInject constructor( @Composable override fun present(): PinnedMessagesListState { - val timelineRoomInfo = remember { + val isDm by room.isDmAsState() + + val timelineRoomInfo = remember(isDm) { TimelineRoomInfo( - isDm = room.isDm, - name = room.displayName, + isDm = isDm, + name = room.info().name, // We don't need to compute those values userHasPermissionToSendMessage = false, userHasPermissionToSendReaction = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index a1fd660251..10db967d3b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -58,6 +58,7 @@ fun PinnedMessagesListView( onEventClick: (event: TimelineItem.Event) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -78,6 +79,7 @@ fun PinnedMessagesListView( onEventClick = onEventClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onErrorDismiss = onBackClick, modifier = Modifier .padding(padding) @@ -112,6 +114,7 @@ private fun PinnedMessagesListContent( onEventClick: (event: TimelineItem.Event) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onErrorDismiss: () -> Unit, modifier: Modifier = Modifier, ) { @@ -130,6 +133,7 @@ private fun PinnedMessagesListContent( onEventClick = onEventClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, ) PinnedMessagesListState.Loading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { @@ -166,6 +170,7 @@ private fun PinnedMessagesListLoaded( onEventClick: (event: TimelineItem.Event) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, modifier: Modifier = Modifier, ) { fun onActionSelected(timelineItemAction: TimelineItemAction, event: TimelineItem.Event) { @@ -216,6 +221,7 @@ private fun PinnedMessagesListLoaded( focusedEventId = null, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onContentClick = onEventClick, onLongClick = ::onMessageLongClick, inReplyToClick = {}, @@ -233,6 +239,7 @@ private fun PinnedMessagesListLoaded( onContentClick = { onEventClick(event) }, onLongClick = { onMessageLongClick(event) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, modifier = contentModifier, onContentLayoutChange = onContentLayoutChange ) @@ -248,6 +255,7 @@ private fun TimelineItemEventContentViewWrapper( timelineProtectionState: TimelineProtectionState, onContentClick: () -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onLongClick: (() -> Unit)?, onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit, modifier: Modifier = Modifier, @@ -264,6 +272,7 @@ private fun TimelineItemEventContentViewWrapper( hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, eventSink = { }, modifier = modifier, onContentClick = onContentClick, @@ -283,5 +292,6 @@ internal fun PinnedMessagesListViewPreview(@PreviewParameter(PinnedMessagesListS onEventClick = { }, onUserDataClick = {}, onLinkClick = {}, + onLinkLongClick = {}, ) } 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 8a496859d0..4061be78c1 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 @@ -34,6 +34,7 @@ import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId @@ -95,7 +96,7 @@ class TimelinePresenter @AssistedInject constructor( val lastReadReceiptId = rememberSaveable { mutableStateOf(null) } - val roomInfo by room.roomInfoFlow.collectAsState(initial = null) + val roomInfo by room.roomInfoFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() @@ -231,15 +232,15 @@ class TimelinePresenter @AssistedInject constructor( val typingNotificationState = typingNotificationPresenter.present() val roomCallState = roomCallStatePresenter.present() - val timelineRoomInfo by remember(typingNotificationState, roomCallState) { + val timelineRoomInfo by remember(typingNotificationState, roomCallState, roomInfo) { derivedStateOf { TimelineRoomInfo( - name = room.displayName, - isDm = room.isDm, + name = roomInfo.name, + isDm = roomInfo.isDm.orFalse(), userHasPermissionToSendMessage = userHasPermissionToSendMessage, userHasPermissionToSendReaction = userHasPermissionToSendReaction, roomCallState = roomCallState, - pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(), + pinnedEventIds = roomInfo.pinnedEventIds.orEmpty(), typingNotificationState = typingNotificationState, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 75f59946c2..b34af8f5a3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -7,6 +7,7 @@ package io.element.android.features.messages.impl.timeline +import android.view.HapticFeedbackConstants import android.view.accessibility.AccessibilityManager import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween @@ -35,12 +36,14 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope 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.rotate import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter @@ -59,6 +62,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState +import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.designsystem.components.dialogs.AlertDialog import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -67,8 +71,21 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.utils.animateScrollToItemCenter import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.transform import kotlinx.coroutines.launch +import timber.log.Timber +import kotlin.time.Duration.Companion.milliseconds @Composable fun TimelineView( @@ -106,6 +123,7 @@ fun TimelineView( } val context = LocalContext.current + val view = LocalView.current // Disable reverse layout when TalkBack is enabled to avoid incorrect ordering issues seen in the current Compose UI version val useReverseLayout = remember { val accessibilityManager = context.getSystemService(AccessibilityManager::class.java) @@ -116,13 +134,28 @@ fun TimelineView( state.eventSink(TimelineEvents.FocusOnEvent(eventId)) } + fun onLinkLongClick(link: String) { + view.performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS + ) + context.copyToClipboard( + link, + context.getString(CommonStrings.common_copied_to_clipboard) + ) + } + + fun prefetchMoreItems() { + state.eventSink(TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS)) + } + // Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms AnimatedVisibility(visible = true, enter = fadeIn()) { Box(modifier) { LazyColumn( modifier = Modifier .fillMaxSize() - .nestedScroll(nestedScrollConnection), + .nestedScroll(nestedScrollConnection) + .testTag(TestTags.timeline), state = lazyListState, reverseLayout = useReverseLayout, contentPadding = PaddingValues(vertical = 8.dp), @@ -141,6 +174,7 @@ fun TimelineView( focusedEventId = state.focusedEventId, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = ::onLinkLongClick, onContentClick = onContentClick, onLongClick = onMessageLongClick, inReplyToClick = ::inReplyToClick, @@ -160,6 +194,11 @@ fun TimelineView( onClearFocusRequestState = ::clearFocusRequestState ) + TimelinePrefetchingHelper( + lazyListState = lazyListState, + prefetch = ::prefetchMoreItems + ) + TimelineScrollHelper( hasAnyEvent = state.hasAnyEvent, lazyListState = lazyListState, @@ -188,6 +227,46 @@ private fun MessageShieldDialog(state: TimelineState) { ) } +@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class) +@Composable +private fun TimelinePrefetchingHelper( + lazyListState: LazyListState, + prefetch: () -> Unit, +) { + val latestPrefetch by rememberUpdatedState(prefetch) + + LaunchedEffect(Unit) { + // We're using snapshot flows for these because using `LaunchedEffect` with `derivedState` doesn't seem to be responsive enough + val firstVisibleItemIndexFlow = snapshotFlow { lazyListState.firstVisibleItemIndex } + val layoutInfoFlow = snapshotFlow { lazyListState.layoutInfo } + val isScrollingFlow = snapshotFlow { lazyListState.isScrollInProgress } + // This value changes too frequently, so we debounce it to avoid unnecessary prefetching. It's the equivalent of a conditional 'throttleLatest' + .conflate() + .transform { isScrolling -> + emit(isScrolling) + if (isScrolling) delay(100.milliseconds) + } + + val isCloseToStartOfLoadedTimelineFlow = combine(layoutInfoFlow, firstVisibleItemIndexFlow) { layoutInfo, firstVisibleItemIndex -> + firstVisibleItemIndex + layoutInfo.visibleItemsInfo.size >= layoutInfo.totalItemsCount - 40 + } + + combine( + isCloseToStartOfLoadedTimelineFlow.distinctUntilChanged(), + isScrollingFlow.distinctUntilChanged(), + ) { needsPrefetch, isScrolling -> + needsPrefetch && isScrolling + } + .distinctUntilChanged() + .collectLatest { needsPrefetch -> + if (needsPrefetch) { + Timber.d("Prefetching pagination with ${lazyListState.layoutInfo.totalItemsCount} items") + latestPrefetch() + } + } + } +} + @Composable private fun BoxScope.TimelineScrollHelper( hasAnyEvent: Boolean, @@ -213,7 +292,7 @@ private fun BoxScope.TimelineScrollHelper( coroutineScope.launch { if (lazyListState.firstVisibleItemIndex > 10) { lazyListState.scrollToItem(0) - } else { + } else if (lazyListState.firstVisibleItemIndex != 0) { lazyListState.animateScrollToItem(0) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt index 685914e4e8..4bcf8df61c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ATimelineItemEventRow.kt @@ -33,6 +33,7 @@ internal fun ATimelineItemEventRow( onEventClick = {}, onLongClick = {}, onLinkClick = {}, + onLinkLongClick = {}, onUserDataClick = {}, inReplyToClick = {}, onReactionClick = { _, _ -> }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 8dccfb586d..9a467eeea7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -25,6 +25,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.isEdited +import io.element.android.features.messages.impl.timeline.model.event.isRedacted import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -44,7 +45,8 @@ fun TimelineEventTimestampView( val hasError = event.localSendState is LocalEventSendState.Failed val hasEncryptionCritical = event.messageShield?.isCritical.orFalse() val isMessageEdited = event.content.isEdited() - val tint = if (hasError || hasEncryptionCritical) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary + val isMessageRedacted = event.content.isRedacted() + val tint = if (hasError || hasEncryptionCritical && !isMessageRedacted) ElementTheme.colors.textCriticalPrimary else ElementTheme.colors.textSecondary Row( modifier = Modifier .padding(PaddingValues(start = TimelineEventTimestampViewDefaults.spacing)) @@ -78,19 +80,22 @@ fun TimelineEventTimestampView( }, ) } - event.messageShield?.let { shield -> - Spacer(modifier = Modifier.width(2.dp)) - Icon( - imageVector = shield.toIcon(), - contentDescription = shield.toText(), - modifier = Modifier + + if (!isMessageRedacted) { + event.messageShield?.let { shield -> + Spacer(modifier = Modifier.width(2.dp)) + Icon( + imageVector = shield.toIcon(), + contentDescription = shield.toText(), + modifier = Modifier .size(15.dp) .clickable { eventSink(TimelineEvents.ShowShieldDialog(shield)) }, - tint = shield.toIconColor(), - ) - Spacer(modifier = Modifier.width(4.dp)) + tint = shield.toIconColor(), + ) + Spacer(modifier = Modifier.width(4.dp)) + } } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt index b3cb657d66..568df86295 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt @@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield @@ -33,5 +34,9 @@ class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider Unit, onLongClick: () -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onUserDataClick: (UserId) -> Unit, inReplyToClick: (EventId) -> Unit, onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit, @@ -138,6 +139,7 @@ fun TimelineItemEventRow( onLongClick = onLongClick, onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, eventSink = eventSink, modifier = contentModifier, onContentLayoutChange = onContentLayoutChange diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt index 12a6125404..8933538b8a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRowTimestampPreview.kt @@ -22,18 +22,25 @@ internal fun TimelineItemEventRowTimestampPreview( @PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event ) = ElementPreview { Column { - val oldContent = event.content as TimelineItemTextContent - listOf( - "Text", - "Text longer, displayed on 1 line", - "Text which should be rendered on several lines", - ).forEach { str -> - ATimelineItemEventRow( - event = event.copy( - content = oldContent.copy( - body = str, - pillifiedBody = str, + when (event.content) { + is TimelineItemTextContent -> listOf( + "Text", + "Text longer, displayed on 1 line", + "Text which should be rendered on several lines", + ).forEach { str -> + ATimelineItemEventRow( + event = event.copy( + content = event.content.copy( + body = str, + pillifiedBody = str, + ), + reactionsState = aTimelineItemReactions(count = 0), ), + ) + } + else -> ATimelineItemEventRow( + event = event.copy( + content = event.content, reactionsState = aTimelineItemReactions(count = 0), ), ) 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 5e538fb483..4a1daa7ba1 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 @@ -46,6 +46,7 @@ fun TimelineItemGroupedEventsRow( inReplyToClick: (EventId) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onReactionClick: (key: String, TimelineItem.Event) -> Unit, onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, @@ -59,6 +60,7 @@ fun TimelineItemGroupedEventsRow( hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, eventSink = eventSink, modifier = contentModifier, onContentClick = null, @@ -87,6 +89,7 @@ fun TimelineItemGroupedEventsRow( inReplyToClick = inReplyToClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, @@ -112,6 +115,7 @@ private fun TimelineItemGroupedEventsRowContent( inReplyToClick: (EventId) -> Unit, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onReactionClick: (key: String, TimelineItem.Event) -> Unit, onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, @@ -125,6 +129,7 @@ private fun TimelineItemGroupedEventsRowContent( hideMediaContent = timelineProtectionState.hideMediaContent(event.eventId), onShowContentClick = { timelineProtectionState.eventSink(TimelineProtectionEvent.ShowContent(event.eventId)) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, eventSink = eventSink, modifier = contentModifier, onContentClick = null, @@ -156,6 +161,7 @@ private fun TimelineItemGroupedEventsRowContent( focusedEventId = focusedEventId, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onContentClick = onClick, onLongClick = onLongClick, inReplyToClick = inReplyToClick, @@ -199,6 +205,7 @@ internal fun TimelineItemGroupedEventsRowContentExpandedPreview() = ElementPrevi isLastOutgoingMessage = false, onClick = {}, onLongClick = {}, + onLinkLongClick = {}, inReplyToClick = {}, onUserDataClick = {}, onLinkClick = {}, @@ -224,6 +231,7 @@ internal fun TimelineItemGroupedEventsRowContentCollapsePreview() = ElementPrevi isLastOutgoingMessage = false, onClick = {}, onLongClick = {}, + onLinkLongClick = {}, inReplyToClick = {}, onUserDataClick = {}, onLinkClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 33cf3cdfcf..525cd436e0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -8,6 +8,8 @@ package io.element.android.features.messages.impl.timeline.components import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -28,7 +30,10 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toPx +import io.element.android.libraries.designsystem.theme.LocalBuildMeta import io.element.android.libraries.designsystem.theme.highlightedMessageBackgroundColor import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId @@ -43,6 +48,7 @@ internal fun TimelineItemRow( focusedEventId: EventId?, onUserDataClick: (UserId) -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onContentClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, inReplyToClick: (EventId) -> Unit, @@ -63,6 +69,7 @@ internal fun TimelineItemRow( onContentClick = { onContentClick(event) }, onLongClick = { onLongClick(event) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, eventSink = eventSink, modifier = contentModifier, onContentLayoutChange = onContentLayoutChange @@ -122,6 +129,7 @@ internal fun TimelineItemRow( onEventClick = { onContentClick(timelineItem) }, onLongClick = { onLongClick(timelineItem) }, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onUserDataClick = onUserDataClick, inReplyToClick = inReplyToClick, onReactionClick = onReactionClick, @@ -150,6 +158,7 @@ internal fun TimelineItemRow( inReplyToClick = inReplyToClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, @@ -167,8 +176,13 @@ private fun Modifier.focusedEvent( focusedEventOffset: Dp ): Modifier { val highlightedLineColor = ElementTheme.colors.textActionAccent + val gradientFirstColor = if (LocalBuildMeta.current.isEnterpriseBuild) { + ElementTheme.colors.textActionAccent.copy(alpha = 0.125f) + } else { + ElementTheme.colors.highlightedMessageBackgroundColor + } val gradientColors = listOf( - ElementTheme.colors.highlightedMessageBackgroundColor, + gradientFirstColor, ElementTheme.colors.bgCanvasDefault, ) val verticalOffset = focusedEventOffset.toPx() @@ -192,3 +206,15 @@ private fun Modifier.focusedEvent( } }.padding(top = 4.dp) } + +@PreviewsDayNight +@Composable +internal fun FocusedEventPreview() = ElementPreview { + Box( + modifier = Modifier + .padding(16.dp) + .fillMaxWidth() + .height(160.dp) + .focusedEvent(0.dp), + ) +} 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 f6b13803ae..6bd0e24908 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 @@ -71,6 +71,7 @@ fun TimelineItemStateEventRow( TimelineItemEventContentView( content = event.content, onLinkClick = {}, + onLinkLongClick = {}, hideMediaContent = false, onShowContentClick = {}, eventSink = eventSink, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt index aa4a006249..2507fb9f01 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt @@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.model.virtual.Timeline import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemRoomBeginningModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel import io.element.android.features.messages.impl.typing.TypingNotificationView +import timber.log.Timber @Composable fun TimelineItemVirtualRow( @@ -45,6 +46,7 @@ fun TimelineItemVirtualRow( TimelineLoadingMoreIndicator(virtual.model.direction) val latestEventSink by rememberUpdatedState(eventSink) LaunchedEffect(virtual.model.timestamp) { + Timber.d("Pagination triggered by load more indicator") latestEventSink(TimelineEvents.LoadMore(virtual.model.direction)) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt index 31f3b8fd54..89a7f6d6ba 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEventContentView.kt @@ -40,6 +40,7 @@ fun TimelineItemEventContentView( onLongClick: (() -> Unit)?, onShowContentClick: () -> Unit, onLinkClick: (url: String) -> Unit, + onLinkLongClick: (String) -> Unit, eventSink: (TimelineEvents.EventFromTimelineItem) -> Unit, modifier: Modifier = Modifier, onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit = {}, @@ -60,6 +61,7 @@ fun TimelineItemEventContentView( content = content, modifier = modifier, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onContentLayoutChange = onContentLayoutChange ) is TimelineItemUnknownContent -> TimelineItemUnknownView( @@ -78,6 +80,7 @@ fun TimelineItemEventContentView( onLongClick = onLongClick, onShowContentClick = onShowContentClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onContentLayoutChange = onContentLayoutChange, modifier = modifier, ) @@ -96,6 +99,7 @@ fun TimelineItemEventContentView( onLongClick = onLongClick, onShowContentClick = onShowContentClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, onContentLayoutChange = onContentLayoutChange, modifier = modifier ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt index ec3186ba9e..2adb2b5f75 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemImageView.kt @@ -65,6 +65,7 @@ fun TimelineItemImageView( onContentClick: (() -> Unit)?, onLongClick: (() -> Unit)?, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onShowContentClick: () -> Unit, onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit, modifier: Modifier = Modifier, @@ -120,6 +121,7 @@ fun TimelineItemImageView( text = caption, style = ElementRichTextEditorStyle.textStyle(), onLinkClickedListener = onLinkClick, + onLinkLongClickedListener = onLinkLongClick, releaseOnDetach = false, onTextLayout = ContentAvoidingLayout.measureLegacyLastTextLine(onContentLayoutChange = onContentLayoutChange), ) @@ -138,6 +140,7 @@ internal fun TimelineItemImageViewPreview(@PreviewParameter(TimelineItemImageCon onContentClick = {}, onLongClick = {}, onLinkClick = {}, + onLinkLongClick = {}, onContentLayoutChange = {}, ) } @@ -152,6 +155,7 @@ internal fun TimelineItemImageViewHideMediaContentPreview() = ElementPreview { onContentClick = {}, onLongClick = {}, onLinkClick = {}, + onLinkLongClick = {}, onContentLayoutChange = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt index 6b2d088cb5..d00c0b58cc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemTextView.kt @@ -46,6 +46,7 @@ import io.element.android.wysiwyg.compose.EditorStyledText fun TimelineItemTextView( content: TimelineItemTextBasedContent, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, modifier: Modifier = Modifier, onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit = {}, ) { @@ -64,6 +65,7 @@ fun TimelineItemTextView( EditorStyledText( text = body, onLinkClickedListener = onLinkClick, + onLinkLongClickedListener = onLinkLongClick, style = ElementRichTextEditorStyle.textStyle(), onTextLayout = ContentAvoidingLayout.measureLegacyLastTextLine(onContentLayoutChange = onContentLayoutChange), releaseOnDetach = false, @@ -115,6 +117,7 @@ internal fun TimelineItemTextViewPreview( TimelineItemTextView( content = content, onLinkClick = {}, + onLinkLongClick = {}, ) } @@ -127,6 +130,7 @@ internal fun TimelineItemTextViewWithLinkifiedUrlPreview() = ElementPreview { TimelineItemTextView( content = content, onLinkClick = {}, + onLinkLongClick = {}, ) } @@ -139,5 +143,6 @@ internal fun TimelineItemTextViewWithLinkifiedUrlAndNestedParenthesisPreview() = TimelineItemTextView( content = content, onLinkClick = {}, + onLinkLongClick = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 343e4be5e8..1fb410fd97 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -74,6 +74,7 @@ fun TimelineItemVideoView( onLongClick: (() -> Unit)?, onShowContentClick: () -> Unit, onLinkClick: (String) -> Unit, + onLinkLongClick: (String) -> Unit, onContentLayoutChange: (ContentAvoidingLayoutData) -> Unit, modifier: Modifier = Modifier, ) { @@ -147,6 +148,7 @@ fun TimelineItemVideoView( .widthIn(min = MIN_HEIGHT_IN_DP.dp * aspectRatio, max = MAX_HEIGHT_IN_DP.dp * aspectRatio), text = caption, onLinkClickedListener = onLinkClick, + onLinkLongClickedListener = onLinkLongClick, style = ElementRichTextEditorStyle.textStyle(), releaseOnDetach = false, onTextLayout = ContentAvoidingLayout.measureLegacyLastTextLine(onContentLayoutChange = onContentLayoutChange), @@ -166,6 +168,7 @@ internal fun TimelineItemVideoViewPreview(@PreviewParameter(TimelineItemVideoCon onContentClick = {}, onLongClick = {}, onLinkClick = {}, + onLinkLongClick = {}, onContentLayoutChange = {}, ) } @@ -180,6 +183,7 @@ internal fun TimelineItemVideoViewHideMediaContentPreview() = ElementPreview { onContentClick = {}, onLongClick = {}, onLinkClick = {}, + onLinkLongClick = {}, onContentLayoutChange = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt index 039a60d670..b666f6ad56 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt @@ -37,13 +37,13 @@ internal fun TimelineItemReadMarkerView( Text( text = stringResource(id = R.string.screen_room_timeline_read_marker_title).uppercase(), style = ElementTheme.typography.fontBodySmMedium, - color = ElementTheme.colors.textSecondary, + color = ElementTheme.colors.textActionAccent, ) HorizontalDivider( modifier = Modifier .fillMaxWidth() .padding(horizontal = 2.dp), - color = ElementTheme.colors.borderInteractivePrimary, + color = ElementTheme.colors.textActionAccent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt index 9eda2e7253..6c20d7326f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContent.kt @@ -92,6 +92,11 @@ fun TimelineItemEventContent.isEdited(): Boolean = when (this) { else -> false } +/** + * Whether the event content has been redacted. + */ +fun TimelineItemEventContent.isRedacted(): Boolean = this is TimelineItemRedactedContent + fun TimelineItemEventContentWithAttachment.duration(): Duration? { return when (this) { is TimelineItemAudioContent -> duration diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index 8404cc6ec6..f078bf28d7 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -19,7 +19,7 @@ "Biblioteca de fotos y vídeos" "Ubicación" "Encuesta" - "Formato de Texto" + "Formato de texto" "El historial de mensajes no está disponible en este momento." "El historial de mensajes no está disponible en esta sala. Verifica este dispositivo para ver tu historial de mensajes." "¿Quieres volver a invitarlos?" @@ -31,6 +31,7 @@ "Añadir emoji" "Este es el principio de %1$s." "Este es el principio de esta conversación." + "Llamada no compatible. Pregunta a la persona que llama si puede utilizar la nueva aplicación Element X." "Mostrar menos" "Mensaje copiado" "No tienes permiso para publicar en esta sala" diff --git a/features/messages/impl/src/main/res/values-eu/translations.xml b/features/messages/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..cfd41379f8 --- /dev/null +++ b/features/messages/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,51 @@ + + + "Jarduerak" + "Banderak" + "Jana eta edana" + "Animaliak eta natura" + "Objektuak" + "Irribartxoak eta jendea" + "Bidaiak eta tokiak" + "Ikurrak" + "Blokeatu erabiltzailea" + "Mezua zure zerbitzariko administratzaileari jakinaraziko zaio. Ezingo dute zifratutako mezurik irakurri." + "Edukia salatzeko arrazoia" + "Kamera" + "Egin argazkia" + "Grabatu bideoa" + "Eranskina" + "Argazki- eta bideo-liburutegia" + "Kokapena" + "Inkesta" + "Testuaren formatua" + "Mezuen historia ez dago erabilgarri gaur-gaurkoz." + "Berriro gonbidatu nahi al dituzu?" + "Bakarrik zaude txat honetan" + "Jakinarazi gela osoari" + "Guztiak" + "Bidali berriro" + "Gehitu emojia" + "Hauxe da %1$s(r)en hasiera" + "Hau da elkarrizketaren hasiera." + "Deia ez da bateragarria. Galdetu deika ari denari Element X aplikazio berria erabil dezakeen." + "Erakutsi gutxiago" + "Mezua kopiatu da" + "Ez duzu gela honetara mezuak bidaltzeko baimenik" + "Erakutsi gutxiago" + "Erakutsi gehiago" + "Berria" + + "Gela aldaketa %1$d" + "%1$d gela aldaketa" + + + "%1$s, %2$s, eta beste %3$d" + "%1$s, %2$s, eta beste %3$d" + + + "%1$s idazten ari da" + "%1$s idazten ari dira" + + "%1$s eta %2$s" + diff --git a/features/messages/impl/src/main/res/values-ka/translations.xml b/features/messages/impl/src/main/res/values-ka/translations.xml index 47261affcc..1c5ad9e8ed 100644 --- a/features/messages/impl/src/main/res/values-ka/translations.xml +++ b/features/messages/impl/src/main/res/values-ka/translations.xml @@ -41,4 +41,13 @@ "%1$dოთახის ცვლილება" "%1$dოთახის ცვლილებები" + + "%1$s, %2$s და %3$d სხვა" + "%1$s%2$sდა %3$d სხვა" + + + "%1$s წერს" + "%1$s წერენ" + + "%1$s და %2$s" 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 f06c1e1683..beb88017f2 100644 --- a/features/messages/impl/src/main/res/values-nb/translations.xml +++ b/features/messages/impl/src/main/res/values-nb/translations.xml @@ -24,12 +24,14 @@ "Meldingshistorikk er ikke tilgjengelig i dette rommet. Bekreft denne enheten for å se meldingshistorikken din." "Vil du invitere dem tilbake?" "Du er alene i denne chatten" + "Varsle hele rommet" "Alle" "Send igjen" "Meldingen din ble ikke sendt" "Legg til emoji" "Dette er begynnelsen på %1$s." "Dette er begynnelsen på denne samtalen." + "Anrop som ikke støttes. Spør om den som ringer kan bruke den nye Element X-appen." "Vis mindre" "Melding kopiert" "Du har ikke tillatelse til å legge ut innlegg i dette rommet" @@ -40,4 +42,13 @@ "%1$d romendring" "%1$d romendringer" + + "%1$s, %2$s og %3$d annet" + "%1$s, %2$s og %3$d andre" + + + "%1$s skriver" + "%1$s skriver" + + "%1$s og %2$s" 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 20b57fa343..1f7d83401e 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 @@ -7,9 +7,7 @@ package io.element.android.features.messages.impl -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test +import androidx.lifecycle.Lifecycle import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.PinUnpinAction import io.element.android.features.messages.impl.actionlist.ActionListEvents @@ -49,6 +47,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags 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.UserId +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.room.MatrixRoom @@ -66,7 +65,9 @@ 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_THROWABLE 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.core.aBuildMeta +import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo @@ -80,6 +81,7 @@ import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.textcomposer.model.aTextEditorStateMarkdown import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.FakeLifecycleOwner import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilTimeout @@ -87,8 +89,8 @@ import io.element.android.tests.testutils.lambda.assert 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 io.element.android.tests.testutils.testCoroutineDispatchers +import io.element.android.tests.testutils.testWithLifecycleOwner import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay @@ -107,9 +109,7 @@ class MessagesPresenterTest { @Test fun `present - initial state`() = runTest { val presenter = createMessagesPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilTimeout().last() assertThat(initialState.roomId).isEqualTo(A_ROOM_ID) assertThat(initialState.roomName).isEqualTo(AsyncData.Success("")) @@ -137,9 +137,7 @@ class MessagesPresenterTest { ) assertThat(room.markAsReadCalls).isEmpty() val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { runCurrent() assertThat(room.setUnreadFlagCalls).isEqualTo(listOf(false)) cancelAndIgnoreRemainingEvents() @@ -166,9 +164,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val initialState = awaitItem() initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) @@ -202,9 +198,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = room, coroutineDispatchers = coroutineDispatchers) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) initialState.eventSink(MessagesEvents.ToggleReaction("👍", AN_EVENT_ID.toEventOrTransactionId())) @@ -225,9 +219,7 @@ class MessagesPresenterTest { onForwardEventClickLambda = onForwardEventClickLambda, ) val presenter = createMessagesPresenter(navigator = navigator) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -240,9 +232,7 @@ class MessagesPresenterTest { val clipboardHelper = FakeClipboardHelper() val event = aMessageEvent() val presenter = createMessagesPresenter(clipboardHelper = clipboardHelper) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.CopyText, event)) skipItems(2) @@ -267,9 +257,7 @@ class MessagesPresenterTest { clipboardHelper = clipboardHelper, matrixRoom = matrixRoom, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.CopyLink, event)) skipItems(2) @@ -283,9 +271,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent())) awaitItem() @@ -303,9 +289,7 @@ class MessagesPresenterTest { @Test fun `present - handle action reply to an event with no id does nothing`() = runTest { val presenter = createMessagesPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, aMessageEvent(eventId = null))) skipItems(1) @@ -318,9 +302,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemImageContent( @@ -360,9 +342,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemVideoContent( @@ -403,9 +383,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() val mediaMessage = aMessageEvent( content = TimelineItemFileContent( @@ -439,9 +417,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Edit, aMessageEvent())) awaitItem() @@ -463,9 +439,7 @@ class MessagesPresenterTest { onEditPollClickLambda = onEditPollClickLambda ) val presenter = createMessagesPresenter(navigator = navigator) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditPoll, aMessageEvent(content = aTimelineItemPollContent()))) awaitItem() @@ -477,9 +451,7 @@ class MessagesPresenterTest { fun `present - handle action end poll`() = runTest { val timelineEventSink = EventsRecorder() val presenter = createMessagesPresenter(timelineEventSink = timelineEventSink) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EndPoll, aMessageEvent(content = aTimelineItemPollContent()))) delay(1) @@ -509,9 +481,7 @@ class MessagesPresenterTest { matrixRoom = matrixRoom, coroutineDispatchers = coroutineDispatchers, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() val messageEvent = aMessageEvent() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Redact, messageEvent)) @@ -529,9 +499,7 @@ class MessagesPresenterTest { onReportContentClickLambda = onReportContentClickLambda ) val presenter = createMessagesPresenter(navigator = navigator) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ReportContent, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -542,9 +510,7 @@ class MessagesPresenterTest { @Test fun `present - handle dismiss action`() = runTest { val presenter = createMessagesPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.Dismiss) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -558,9 +524,7 @@ class MessagesPresenterTest { onShowEventDebugInfoClickLambda = onShowEventDebugInfoClickLambda ) val presenter = createMessagesPresenter(navigator = navigator) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ViewSource, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) @@ -572,25 +536,24 @@ class MessagesPresenterTest { fun `present - shows prompt to reinvite users in DM`() = runTest { val room = FakeMatrixRoom( sessionId = A_SESSION_ID, - isDirect = true, - activeMemberCount = 1L, canUserSendMessageResult = { _, _ -> Result.success(true) }, canRedactOwnResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, typingNoticeResult = { Result.success(Unit) }, canUserPinUnpinResult = { Result.success(true) }, - ) + ).apply { + givenRoomInfo(aRoomInfo(isDirect = true, joinedMembersCount = 1, activeMembersCount = 1)) + } val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() // Initially the composer doesn't have focus, so we don't show the alert assertThat(initialState.showReinvitePrompt).isFalse() // When the input field is focused we show the alert (initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true - skipItems(1) + // Skip intermediate states + skipItems(2) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isTrue() // If it's dismissed then we stop showing the alert @@ -605,22 +568,22 @@ class MessagesPresenterTest { fun `present - doesn't show reinvite prompt in non-direct room`() = runTest { val room = FakeMatrixRoom( sessionId = A_SESSION_ID, - isDirect = false, - activeMemberCount = 1L, canUserSendMessageResult = { _, _ -> Result.success(true) }, canRedactOwnResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, typingNoticeResult = { Result.success(Unit) }, canUserPinUnpinResult = { Result.success(true) }, - ) + ).apply { + givenRoomInfo(aRoomInfo(isDirect = false, joinedMembersCount = 1, activeMembersCount = 1)) + } val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() (initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true + // Skip intermediate events + skipItems(1) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() } @@ -630,22 +593,22 @@ class MessagesPresenterTest { fun `present - doesn't show reinvite prompt if other party is present`() = runTest { val room = FakeMatrixRoom( sessionId = A_SESSION_ID, - isDirect = true, - activeMemberCount = 2L, canUserSendMessageResult = { _, _ -> Result.success(true) }, canRedactOwnResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, typingNoticeResult = { Result.success(Unit) }, canUserPinUnpinResult = { Result.success(true) }, - ) + ).apply { + givenRoomInfo(aRoomInfo(isDirect = true, joinedMembersCount = 2, activeMembersCount = 2)) + } val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() assertThat(initialState.showReinvitePrompt).isFalse() (initialState.composerState.textEditorState as TextEditorState.Markdown).state.hasFocus = true + // Skip intermediate events + skipItems(1) val focusedState = awaitItem() assertThat(focusedState.showReinvitePrompt).isFalse() } @@ -673,9 +636,7 @@ class MessagesPresenterTest { ) ) val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) @@ -710,9 +671,7 @@ class MessagesPresenterTest { ) ) val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) @@ -739,9 +698,7 @@ class MessagesPresenterTest { ) room.givenRoomMembersState(MatrixRoomMembersState.Unknown) val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) skipItems(1) @@ -773,15 +730,15 @@ class MessagesPresenterTest { ) ) val presenter = createMessagesPresenter(matrixRoom = room) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilTimeout().last() initialState.eventSink(MessagesEvents.InviteDialogDismissed(InviteDialogAction.Invite)) + val loadingState = consumeItemsUntilPredicate { state -> state.inviteProgress.isLoading() }.last() assertThat(loadingState.inviteProgress.isLoading()).isTrue() + val failureState = consumeItemsUntilPredicate { state -> state.inviteProgress.isFailure() }.last() @@ -806,9 +763,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val state = awaitItem() assertThat(state.userEventPermissions.canSendMessage).isTrue() @@ -832,9 +787,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { // Default value assertThat(awaitItem().userEventPermissions.canSendMessage).isTrue() assertThat(awaitItem().userEventPermissions.canSendMessage).isFalse() @@ -852,9 +805,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilPredicate { it.userEventPermissions.canRedactOwn }.last() assertThat(initialState.userEventPermissions.canRedactOwn).isTrue() assertThat(initialState.userEventPermissions.canRedactOther).isFalse() @@ -873,9 +824,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = matrixRoom) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = consumeItemsUntilPredicate { it.userEventPermissions.canRedactOther }.last() assertThat(initialState.userEventPermissions.canRedactOwn).isFalse() assertThat(initialState.userEventPermissions.canRedactOther).isTrue() @@ -889,9 +838,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() val poll = aMessageEvent( content = aTimelineItemPollContent() @@ -925,9 +872,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = room, analyticsService = analyticsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val messageEvent = aMessageEvent( content = aTimelineItemTextContent() ) @@ -965,9 +910,7 @@ class MessagesPresenterTest { canUserPinUnpinResult = { Result.success(true) }, ) val presenter = createMessagesPresenter(matrixRoom = room, analyticsService = analyticsService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { val messageEvent = aMessageEvent( content = aTimelineItemTextContent() ) @@ -1000,7 +943,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( messageComposerPresenter = { aMessageComposerState(eventSink = composerRecorder) }, ) - presenter.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditCaption, messageEvent)) awaitItem() @@ -1030,7 +973,7 @@ class MessagesPresenterTest { initialState = mapOf(FeatureFlags.MediaCaptionWarning.key to false) ) ) - presenter.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.EditCaption, messageEvent)) awaitItem() @@ -1057,7 +1000,7 @@ class MessagesPresenterTest { caption = null, ) ) - presenter.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.AddCaption, messageEvent)) awaitItem() @@ -1087,7 +1030,7 @@ class MessagesPresenterTest { caption = null, ) ) - presenter.test { + presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.AddCaption, messageEvent)) awaitItem() @@ -1126,7 +1069,7 @@ class MessagesPresenterTest { val presenter = createMessagesPresenter( matrixRoom = room, ) - presenter.test { + presenter.testWithLifecycleOwner { skipItems(1) val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.RemoveCaption, messageEvent)) @@ -1140,7 +1083,7 @@ class MessagesPresenterTest { content = aTimelineItemTextContent() ) val presenter = createMessagesPresenter() - presenter.test { + presenter.testWithLifecycleOwner { skipItems(1) val initialState = awaitItem() initialState.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ViewInTimeline, messageEvent)) @@ -1148,6 +1091,36 @@ class MessagesPresenterTest { } } + @Test + fun `present - when room is encrypted and a DM, the DM user's identity state is fetched onResume`() = runTest { + val room = FakeMatrixRoom( + sessionId = A_SESSION_ID, + canUserSendMessageResult = { _, _ -> Result.success(true) }, + canRedactOwnResult = { Result.success(true) }, + canRedactOtherResult = { Result.success(true) }, + canUserJoinCallResult = { Result.success(true) }, + typingNoticeResult = { Result.success(Unit) }, + canUserPinUnpinResult = { Result.success(true) }, + initialRoomInfo = aRoomInfo(isDirect = true, isEncrypted = true) + ).apply { + givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(aRoomMember(userId = A_SESSION_ID), aRoomMember(userId = A_USER_ID_2)))) + } + val encryptionService = FakeEncryptionService(getUserIdentityResult = { Result.success(IdentityState.Verified) }) + + val presenter = createMessagesPresenter(matrixRoom = room, encryptionService = encryptionService) + val lifecycleOwner = FakeLifecycleOwner() + presenter.testWithLifecycleOwner(lifecycleOwner) { + val initialState = awaitItem() + assertThat(initialState.dmUserVerificationState).isNull() + + skipItems(1) + ensureAllEventsConsumed() + + lifecycleOwner.givenState(Lifecycle.State.RESUMED) + assertThat(awaitItem().dmUserVerificationState).isEqualTo(IdentityState.Verified) + } + } + private fun TestScope.createMessagesPresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), matrixRoom: MatrixRoom = FakeMatrixRoom( @@ -1172,6 +1145,7 @@ class MessagesPresenterTest { textEditorState = aTextEditorStateMarkdown(initialText = "", initialFocus = false) ) }, + encryptionService: FakeEncryptionService = FakeEncryptionService(), actionListEventSink: (ActionListEvents) -> Unit = {}, ): MessagesPresenter { return MessagesPresenter( @@ -1197,6 +1171,7 @@ class MessagesPresenterTest { htmlConverterProvider = FakeHtmlConverterProvider(), timelineController = TimelineController(matrixRoom), permalinkParser = permalinkParser, + encryptionService = encryptionService, analyticsService = analyticsService, ) } 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 a7c76d73e2..9f46637948 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 @@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMess import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo import io.element.android.features.messages.impl.timeline.aTimelineState @@ -50,6 +51,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.testtags.TestTags @@ -126,6 +128,9 @@ class MessagesViewTest { fun `clicking on an Event invoke expected callback`() { val eventsRecorder = EventsRecorder(expectEvents = false) val state = aMessagesState( + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), eventSink = eventsRecorder ) val timelineItem = state.timelineState.timelineItems.first() @@ -182,6 +187,9 @@ class MessagesViewTest { canSendReaction = userHasPermissionToSendReaction, canPinUnpin = userCanPinEvent, ), + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event rule.setMessagesView( @@ -349,7 +357,10 @@ class MessagesViewTest { fun `clicking on a reaction emits the expected Event`() { val eventsRecorder = EventsRecorder() val state = aMessagesState( - eventSink = eventsRecorder + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), + eventSink = eventsRecorder, ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event rule.setMessagesView( @@ -363,6 +374,9 @@ class MessagesViewTest { fun `long clicking on a reaction emits the expected Event`() { val eventsRecorder = EventsRecorder() val state = aMessagesState( + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), reactionSummaryState = aReactionSummaryState( target = null, eventSink = eventsRecorder, @@ -380,6 +394,9 @@ class MessagesViewTest { fun `clicking on more reaction emits the expected Event`() { val eventsRecorder = EventsRecorder() val state = aMessagesState( + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), customReactionState = aCustomReactionState( eventSink = eventsRecorder, ), @@ -396,7 +413,11 @@ class MessagesViewTest { @Test fun `clicking on more reaction from action list emits the expected Event`() { val eventsRecorder = EventsRecorder() - val state = aMessagesState() + val state = aMessagesState( + timelineState = aTimelineState( + timelineItems = aTimelineItemList(aTimelineItemTextContent()), + ), + ) val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event val stateWithActionListState = state.copy( actionListState = anActionListState( @@ -538,7 +559,7 @@ private fun AndroidComposeTestRule.setMessa onCreatePollClick = onCreatePollClick, onJoinCallClick = onJoinCallClick, onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick, - knockRequestsBannerView = {} + knockRequestsBannerView = {}, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt index 29f64b831f..2a82db690e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStatePresenterTest.kt @@ -19,6 +19,7 @@ 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.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -44,7 +45,9 @@ class IdentityChangeStatePresenterTest { @Test fun `present - when the room emits identity change, the presenter emits new state`() = runTest { - val room = FakeMatrixRoom(isEncrypted = true) + val room = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(isEncrypted = true)) + } val presenter = createIdentityChangeStatePresenter(room) presenter.test { val initialState = awaitItem() @@ -67,10 +70,7 @@ class IdentityChangeStatePresenterTest { @Test fun `present - when the clear room emits identity change, the presenter does not emit new state`() = runTest { - val room = FakeMatrixRoom( - isEncrypted = false, - enableEncryptionResult = { Result.success(Unit) } - ) + val room = FakeMatrixRoom(enableEncryptionResult = { Result.success(Unit) }) val presenter = createIdentityChangeStatePresenter(room) presenter.test { val initialState = awaitItem() @@ -85,8 +85,9 @@ class IdentityChangeStatePresenterTest { ) // No item emitted. expectNoEvents() - // Room become encrypted. - room.enableEncryption() + // Room becomes encrypted. + room.givenRoomInfo(aRoomInfo(isEncrypted = true)) + val finalItem = awaitItem() assertThat(finalItem.roomMemberIdentityStateChanges).hasSize(1) val value = finalItem.roomMemberIdentityStateChanges.first() @@ -99,7 +100,7 @@ class IdentityChangeStatePresenterTest { @Test fun `present - when the room emits identity change, the presenter emits new state with member details`() = runTest { - val room = FakeMatrixRoom(isEncrypted = true).apply { + val room = FakeMatrixRoom().apply { givenRoomMembersState( MatrixRoomMembersState.Ready( listOf( @@ -110,6 +111,7 @@ class IdentityChangeStatePresenterTest { ).toImmutableList() ) ) + givenRoomInfo(aRoomInfo(isEncrypted = true)) } val presenter = createIdentityChangeStatePresenter(room) presenter.test { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt index ca07cc93cd..4f67049f98 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/crypto/identity/IdentityChangeStateViewTest.kt @@ -15,6 +15,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.designsystem.components.avatar.anAvatarData import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.ui.room.IdentityRoomMember +import io.element.android.libraries.matrix.ui.room.RoomMemberIdentityStateChange import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index b7ba51192b..07205590c8 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -63,6 +63,7 @@ import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache @@ -998,7 +999,6 @@ class MessageComposerPresenterTest { val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN) var canUserTriggerRoomNotificationResult = true val room = FakeMatrixRoom( - isDirect = false, canUserTriggerRoomNotificationResult = { Result.success(canUserTriggerRoomNotificationResult) }, typingNoticeResult = { Result.success(Unit) } ).apply { @@ -1007,6 +1007,7 @@ class MessageComposerPresenterTest { persistentListOf(currentUser, invitedUser, bob, david), ) ) + givenRoomInfo(aRoomInfo(isDirect = false)) } val flagsService = FakeFeatureFlagService( mapOf( @@ -1060,9 +1061,6 @@ class MessageComposerPresenterTest { val bob = aRoomMember(userId = A_USER_ID_2, membership = RoomMembershipState.JOIN) val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN) val room = FakeMatrixRoom( - isDirect = true, - activeMemberCount = 2, - isEncrypted = true, canUserTriggerRoomNotificationResult = { Result.success(true) }, typingNoticeResult = { Result.success(Unit) } ).apply { @@ -1071,6 +1069,12 @@ class MessageComposerPresenterTest { persistentListOf(currentUser, invitedUser, bob, david), ) ) + givenRoomInfo( + aRoomInfo( + isDirect = true, + activeMembersCount = 2, + ) + ) } val flagsService = FakeFeatureFlagService( mapOf( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt index 3257a6b503..0906fca360 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListViewTest.kt @@ -100,6 +100,7 @@ private fun AndroidComposeTestRule.setPinne onEventClick: (event: TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(), onUserDataClick: (UserId) -> Unit = EnsureNeverCalledWithParam(), onLinkClick: (String) -> Unit = EnsureNeverCalledWithParam(), + onLinkLongClick: (String) -> Unit = EnsureNeverCalledWithParam(), ) { setSafeContent { PinnedMessagesListView( @@ -108,6 +109,7 @@ private fun AndroidComposeTestRule.setPinne onEventClick = onEventClick, onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, ) } } 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 61e742f872..fba34f7fab 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 @@ -11,14 +11,18 @@ import androidx.activity.ComponentActivity import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performScrollToIndex import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.messages.impl.timeline.components.aCriticalShield import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemUnknownContent import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingIndicatorModel import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState +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.UserId import io.element.android.libraries.matrix.api.timeline.Timeline @@ -31,6 +35,7 @@ import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.clickOn import io.element.android.tests.testutils.setSafeContent import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import org.junit.Rule import org.junit.Test import org.junit.rules.TestRule @@ -114,8 +119,6 @@ class TimelineViewTest { rule.onNodeWithContentDescription(contentDescription).performClick() eventsRecorder.assertList( listOf( - TimelineEvents.OnScrollFinished(0), - TimelineEvents.OnScrollFinished(0), TimelineEvents.OnScrollFinished(0), TimelineEvents.ShowShieldDialog(MessageShield.UnverifiedIdentity(true)), ) @@ -135,6 +138,37 @@ class TimelineViewTest { rule.clickOn(CommonStrings.action_ok) eventsRecorder.assertSingle(TimelineEvents.HideShieldDialog) } + + @Test + fun `scrolling near to the start of the loaded items triggers a pre-fetch`() { + val eventsRecorder = EventsRecorder() + val items = List(200) { + aTimelineItemEvent( + eventId = EventId("\$event_$it"), + content = aTimelineItemUnknownContent(), + ) + }.toPersistentList() + + rule.setTimelineView( + state = aTimelineState( + timelineItems = items, + eventSink = eventsRecorder, + focusedEventIndex = -1, + isLive = false, + ), + ) + + rule.onNodeWithTag("timeline").performScrollToIndex(180) + + rule.mainClock.advanceTimeBy(1000) + + eventsRecorder.assertList( + listOf( + TimelineEvents.OnScrollFinished(firstIndex = 0), + TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS), + ) + ) + } } private fun AndroidComposeTestRule.setTimelineView( diff --git a/features/onboarding/impl/src/main/res/values-eu/translations.xml b/features/onboarding/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..18c436a2b7 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,9 @@ + + + "Hasi saioa eskuz" + "Hasi saioa QR kodearekin" + "Sortu kontua" + "Ongi etorri inoizko %1$s azkarrenera. Abiaduraz eta sinpletasunaz gainkargatua." + "Ongi etorri %1$s-ra. Abiaduraz eta sinpletasunez gainezka." + "Egon zure saltsan" + 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 04f927086b..5a86e476a9 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 @@ -22,6 +22,7 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService +import java.util.concurrent.atomic.AtomicBoolean @ContributesNode(RoomScope::class) class CreatePollNode @AssistedInject constructor( @@ -34,7 +35,16 @@ class CreatePollNode @AssistedInject constructor( private val inputs: Inputs = inputs() - private val presenter = presenterFactory.create(backNavigator = ::navigateUp, mode = inputs.mode) + private var isNavigatingUp = AtomicBoolean(false) + + private val presenter = presenterFactory.create( + backNavigator = { + if (isNavigatingUp.compareAndSet(false, true)) { + navigateUp() + } + }, + mode = inputs.mode, + ) init { lifecycle.subscribe( diff --git a/features/poll/impl/src/main/res/values-eu/translations.xml b/features/poll/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..768854d44c --- /dev/null +++ b/features/poll/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,19 @@ + + + "Gehitu aukera" + "Erakutsi emaitzak inkesta amaitutakoan soilik" + "Ezkutatu botoak" + "%1$d. aukera" + "Zure aldaketak ez dira gorde. Ziur itzuli nahi duzula?" + "Galdera edo gaia" + "Zeri buruzko inkesta da?" + "Sortu inkesta" + "Ziur inkesta hau ezabatu nahi duzula?" + "Ezabatu inkesta" + "Editatu inkesta" + "Ezin da abian dagoen inkestarik aurkitu." + "Ezin da iraungitako inkestarik aurkitu." + "Abian direnak" + "Iraungitakoak" + "Inkestak" + diff --git a/features/poll/impl/src/main/res/values-ka/translations.xml b/features/poll/impl/src/main/res/values-ka/translations.xml index 05d046a246..fe70a883a8 100644 --- a/features/poll/impl/src/main/res/values-ka/translations.xml +++ b/features/poll/impl/src/main/res/values-ka/translations.xml @@ -4,6 +4,7 @@ "შედეგების ჩვენება მხოლოდ გამოკითხვის დასრულების შემდეგ" "ხმების დამალვა" "ვარიანტი %1$d" + "თქვენი ცვლილებები არაა შენახული. დარწმუნებული ხართ დაბრუნებაში?" "კითხვა ან თემა" "რას ეხება გამოკითხვა?" "გამოკითხვის შექმნა" diff --git a/features/poll/impl/src/main/res/values-nb/translations.xml b/features/poll/impl/src/main/res/values-nb/translations.xml index 6505101bad..29af6a274b 100644 --- a/features/poll/impl/src/main/res/values-nb/translations.xml +++ b/features/poll/impl/src/main/res/values-nb/translations.xml @@ -11,4 +11,8 @@ "Er du sikker på at du vil slette denne avstemningen?" "Slett avstemning" "Rediger avstemning" + "Finner ingen pågående avstemninger." + "Kan ikke finne noen tidligere avstemninger." + "Pågående" + "Avstemninger" diff --git a/features/preferences/impl/src/main/res/values-es/translations.xml b/features/preferences/impl/src/main/res/values-es/translations.xml index db20929504..1ae1660faa 100644 --- a/features/preferences/impl/src/main/res/values-es/translations.xml +++ b/features/preferences/impl/src/main/res/values-es/translations.xml @@ -1,16 +1,21 @@ + "Para asegurarte de que nunca te pierdas una llamada importante, modifica tus ajustes para permitir notificaciones a pantalla completa cuando el teléfono esté bloqueado." + "Mejora tu experiencia de llamada" "Elige cómo recibir las notificaciones" "Modo desarrollador" "Habilita para tener acceso a características y funcionalidades para desarrolladores." "URL base personalizada de Element Call" "Define una URL base personalizada para Element Call." "URL no válida, asegúrate de incluir el protocolo (http/https) y la dirección correcta." + "Sube fotos y vídeos más rápido y reduce el uso de datos" + "Optimizar la calidad de los medios" + "Proveedor de notificaciones push" "Desactiva el editor de texto enriquecido para escribir Markdown manualmente." "Confirmaciones de lectura" "Si se desactiva, las confirmaciones de lectura no se enviarán a nadie. Seguirás recibiendo confirmaciones de lectura de otros usuarios." "Compartir presencia" - "Si se desactiva, no podrás enviar ni recibir confirmaciones de lectura ni notificaciones de escritura" + "Si se desactiva, no podrás enviar ni recibir confirmaciones de lectura ni notificaciones de escritura." "Habilita la opción para ver el contenido en bruto del mensaje en la cronología." "No tienes usuarios bloqueados" "Desbloquear" @@ -50,4 +55,6 @@ Si continúas, es posible que algunos de tus ajustes cambien." "ajustes del sistema" "Notificaciones del sistema desactivadas" "Notificaciones" + "Solucionar problemas" + "Solucionar problemas con las notificaciones" diff --git a/features/preferences/impl/src/main/res/values-eu/translations.xml b/features/preferences/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..4caeb4f6db --- /dev/null +++ b/features/preferences/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,48 @@ + + + "Dei garrantzitsurik galduko ez duzula ziurtatzeko, aldatu ezarpenak telefonoa blokeatuta dagoenean pantaila osoko jakinarazpenak baimentzeko." + "Hobetu deien esperientzia" + "Aukeratu jakinarazpenak nola jaso" + "Garatzaile modua" + "Gaitu garatzaileentzako ezaugarrietarako eta funtzionalitateetarako sarbidea izateko." + "Push jakinarazpen hornitzailea" + "Desgaitu testu aberatseko editorea Markdown eskuz idazteko." + "Irakurketa-agiriak" + "Desaktibatutaz gero, ez zaizkio inori bidaliko mezuak irakurri izanaren agiriak. Beste erabiltzaile batzuen irakurketa-agiriak jasoko dituzu oraindik ere." + "Partekatu presentzia" + "Gaitu aukera mezuaren iturria denbora-lerroan ikusteko." + "Ez duzu erabiltzailerik blokeatu" + "Desblokeatu" + "Beraien mezu guztiak berriro ikusteko aukera izango duzu." + "Desblokeatu erabiltzailea" + "Desblokeatzen…" + "Pantaila-izena" + "Zure pantaila-izena" + "Errore ezezagun bat aurkitu da eta ezin izan da informazioa aldatu." + "Ezin da profila eguneratu" + "Editatu profila" + "Profila eguneratzen…" + "Ezarpen gehiago" + "Audio eta bideo deiak" + "Konfigurazioa ez dator bat" + "Txat zuzenak" + "Errorea gertatu da jakinarazpen-ezarpena eguneratzean." + "Mezu guztiak" + "Aipamenak eta hitz gakoak soilik" + "Zuzeneko txatetan, jakinarazi" + "Taldeko txatetan, jakinarazi" + "Gaitu jakinarazpenak gailu honetan" + "Konfigurazioa ez da zuzendu; saiatu berriro." + "Taldeko txatak" + "Gonbidapenak" + "Zure zerbitzaria ez da bateragarria enkriptatutako gelen aukerarekin; litekeena da gela batzuetako jakinarazpenak ez jasotzea." + "Aipamenak" + "Guztia" + "Aipamenak" + "Jakinarazi" + "Jakinarazi @gelan" + "Jakinarazpenak jasotzeko, aldatu %1$s." + "sistemaren ezarpenak" + "Sistemaren jakinarazpenak desaktibatuta daude" + "Jakinarazpenak" + diff --git a/features/preferences/impl/src/main/res/values-ka/translations.xml b/features/preferences/impl/src/main/res/values-ka/translations.xml index 89c39c1c44..1efe197267 100644 --- a/features/preferences/impl/src/main/res/values-ka/translations.xml +++ b/features/preferences/impl/src/main/res/values-ka/translations.xml @@ -7,10 +7,16 @@ "დააყენეთ საბაზისო URL Element-ის ზარებისათვის." "არასწორი URL, გთხოვთ, დარწმუნდეთ, რომ შეიტანეთ პროტოკოლი (http/https) და სწორი მისამართი." "გამორთეთ მდიდარი ტექსტის რედაქტორი, რათა ხელით აკრიფოთ Markdown." + "წაკითხვის შეტყობინებები" + "გამორთვის შემთხვევაში სხვები ვერ მიიღებენ თქვენი წაკითხვის შეტყობინებებს. თქვენ მაინც მიიღებთ სხვების წაკითხვის შეტყობინებებს." + "მყოფობის გაზიარება" + "გამორთვის შემთხვევაში თქვენ ვერ მიიღებთ და ვერ გაგზავნით წაკითხვის ან წერის შეტყობინებებს." "ჩართეთ ოპცია რათა შეტყობინების წყაროს დროის ისტორია ნახოთ." + "თქვენ არ დაგიბლოკავთ მომხმარებლები" "განბლოკვა" "თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას." "Მომხმარებლის განბლოკვა" + "განბლოკვა…" "ნაჩვენები სახელი" "თქვენი ნაჩვენები სახელი" "დაფიქსირდა უცნობი შეცდომა და ინფორმაციის შეცვლა ვერ მოხერხდა." @@ -44,4 +50,6 @@ "სისტემის პარამეტრები" "სისტემის შეტყობინებები გამორთულია" "შეტყობინებები" + "პრობლემების გადაჭრა" + "პრობლემების გადაჭრის შეტყობინებები" 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 68f23bbd43..e8fc907026 100644 --- a/features/preferences/impl/src/main/res/values-nb/translations.xml +++ b/features/preferences/impl/src/main/res/values-nb/translations.xml @@ -1,12 +1,24 @@ + "For å sikre at du aldri går glipp av en viktig samtale, må du endre innstillingene dine for å tillate fullskjermvarsler når telefonen er låst." + "Forbedre samtaleopplevelsen din" "Velg hvordan du vil motta varsler" "Utviklermodus" "Aktiver for å få tilgang til funksjoner og funksjonalitet for utviklere." + "Ugyldig URL. Pass på at du inkluderer protokollen (http/https) og riktig adresse." + "Last opp bilder og videoer raskere og reduser databruken" + "Optimaliser mediekvaliteten" "Deaktiver rik tekstredigering for å skrive Markdown manuelt." + "Lesebekreftelser" + "Hvis slått av, sendes ikke lesebekreftelsene dine til noen. Du vil fortsatt motta lesebekreftelser fra andre brukere." + "Del tilstedeværelse" + "Hvis slått av, kan du ikke sende eller motta lesebekreftelser eller skrivevarsler." + "Aktiver alternativet for å vise meldingskilden på tidslinjen." + "Du har ingen blokkerte brukere" "Fjern blokkering" "Du vil kunne se alle meldingene fra dem igjen." "Fjern blokkering av bruker" + "Fjerner blokkering …" "Visningsnavn" "Ditt visningsnavn" "Det oppstod en ukjent feil, og informasjonen kunne ikke endres." @@ -29,11 +41,17 @@ Hvis du fortsetter, kan noen av innstillingene dine endres." "Aktiver varsler på denne enheten" "Konfigurasjonen er ikke korrigert, prøv igjen." "Gruppechatter" + "Invitasjoner" + "Hjemmeserveren din støtter ikke dette alternativet i krypterte rom, og det kan hende at du ikke blir varslet i enkelte rom." + "Omtaler" "Alle" + "Omtaler" "Varsle meg om" "Gi meg varsel på @room" "For å motta varsler, vennligst endre %1$s." "systeminnstillinger" "Systemvarsler er slått av" "Varslinger" + "Feilsøk" + "Feilsøk varsler" diff --git a/features/rageshake/api/src/main/res/values-eu/translations.xml b/features/rageshake/api/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..48f9914a30 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-eu/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s kraskatu zen azkenekoz erabili zenean. Gurekin partekatu nahi al duzu kraskatzearen txostena?" + "Frustrazioaren eraginez mugikorra astintzen ari zarela dirudi. Erroreen berri emateko pantaila ireki nahi al duzu?" + diff --git a/features/rageshake/impl/src/main/res/values-eu/translations.xml b/features/rageshake/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..1ba3ba9a6f --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,16 @@ + + + "Erantsi pantaila-argazkia" + "Galderarik baduzu, nirekin jar zaitezke harremanetan." + "Jarri nirekin harremanetan" + "Editatu pantaila-argazkia" + "Deskribatu arazoa. Zer egin duzu? Zer espero zenuen gertatzea? Benetan gertatu dena. Eman ahalik eta xehetasun gehien." + "Deskribatu arazoa…" + "Ahal izanez gero, idatzi deskribapena ingelesez." + "Deskribapena laburregia da; eman gertatutakoari buruzko xehetasun gehiago. Eskerrik asko!" + "Bidali kraskaduraren erregistroak" + "Baimendu erregistroak" + "Bidali pantaila-argazkia" + "%1$s kraskatu zen azkenekoz erabili zenean. Gurekin partekatu nahi al duzu kraskatzearen txostena?" + "Ikusi erregistroak" + diff --git a/features/rageshake/impl/src/main/res/values-ka/translations.xml b/features/rageshake/impl/src/main/res/values-ka/translations.xml index 6f0afb86b1..3fd04f0dd2 100644 --- a/features/rageshake/impl/src/main/res/values-ka/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ka/translations.xml @@ -7,9 +7,11 @@ "გთხოვთ, აღწეროთ პრობლემა. რა გააკეთე? რა შედეგს ელოდებოდით? რა მოხდა სინამდვილეში? გთხოვთ, ყველაფერი დაწვრილებით თქვათ." "აღწერეთ პრობლემა…" "თუ შესაძლებელია, გთხოვთ, დაწეროთ აღწერა ინგლისურ ენაზე." + "აღწერა ძალიან მოკლეა, გთხოვთ მოგვაწოდოთ მეტი დეტალი მომხდარის შესახებ. მადლობა!" "გაუმართაობის ჟურნალის გაგზავნა" "ჟურნალების დაშვება" "ეკრანის ანაბეჭდის გაგზავნა" "ჟურნალები თქვენს შეტყობინებაში შევა იმაში დასარწმუნებლად, რომ ყველაფერი სწორად მუშაობს. ჟურნალების გარეშე გასაგზავნად გათიშეთ ეს პარამეტრი." "%1$s ავარიულად გაითიშა ბოლოს გამოიყენებისას. გსურთ, გამოგვიგზავნოთ ავარიული გათიშვის ჟურნალი?" + "ჟურნალის ნახვა" 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 995bcf945a..34a6ade303 100644 --- a/features/rageshake/impl/src/main/res/values-nb/translations.xml +++ b/features/rageshake/impl/src/main/res/values-nb/translations.xml @@ -7,9 +7,11 @@ "Vennligst beskriv problemet. Hva har du gjort? Hva forventet du skulle skje? Hva som faktisk skjedde. Vær så detaljert som mulig." "Beskriv problemet…" "Hvis mulig, vennligst skriv beskrivelsen på engelsk." + "Beskrivelsen er for kort, vennligst gi flere detaljer om hva som skjedde. Takk skal du ha!" "Send krasjlogger" "Tillat logger" "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?" + "Vis logger" diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt index 6e8286e76c..ef2cc47ecc 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverView.kt @@ -51,7 +51,7 @@ fun RoomAliasResolverView( ) { HeaderFooterPage( containerColor = Color.Transparent, - paddingValues = PaddingValues( + contentPadding = PaddingValues( horizontal = 16.dp, vertical = 32.dp ), diff --git a/features/roomaliasresolver/impl/src/main/res/values-es/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..73cc4936ea --- /dev/null +++ b/features/roomaliasresolver/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,5 @@ + + + "No hemos podido mostrar la vista previa de esta sala" + "No se pudo resolver el alias de la sala." + diff --git a/features/roomaliasresolver/impl/src/main/res/values-fi/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-fi/translations.xml index 20b00dcdd7..84e0615472 100644 --- a/features/roomaliasresolver/impl/src/main/res/values-fi/translations.xml +++ b/features/roomaliasresolver/impl/src/main/res/values-fi/translations.xml @@ -1,4 +1,5 @@ + "Emme voineet näyttää tämän huoneen esikatselua" "Huoneen aliaksen ratkaiseminen epäonnistui." diff --git a/features/roomaliasresolver/impl/src/main/res/values-it/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-it/translations.xml index e52051ae93..5860ac2939 100644 --- a/features/roomaliasresolver/impl/src/main/res/values-it/translations.xml +++ b/features/roomaliasresolver/impl/src/main/res/values-it/translations.xml @@ -1,4 +1,5 @@ + "Non è stato possibile visualizzare l\'anteprima di questa stanza" "Impossibile risolvere l\'alias della stanza." diff --git a/features/roomaliasresolver/impl/src/main/res/values-sv/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-sv/translations.xml index 8bcfb3f809..d2da1cae88 100644 --- a/features/roomaliasresolver/impl/src/main/res/values-sv/translations.xml +++ b/features/roomaliasresolver/impl/src/main/res/values-sv/translations.xml @@ -1,4 +1,5 @@ + "Vi kunde inte visa förhandsgranskningen av rummet" "Misslyckades med att slå upp rumsalias." diff --git a/features/roomaliasresolver/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomaliasresolver/impl/src/main/res/values-zh-rTW/translations.xml index 9aa1586b8a..0e65740521 100644 --- a/features/roomaliasresolver/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomaliasresolver/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,4 +1,5 @@ + "我們無法顯示此聊天室的預覽" "無法解析聊天室別名。" diff --git a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt index 4c3ddddb17..d04d40b4ef 100644 --- a/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt +++ b/features/roomcall/impl/src/main/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenter.kt @@ -26,12 +26,12 @@ class RoomCallStatePresenter @Inject constructor( ) : Presenter { @Composable override fun present(): RoomCallState { - val roomInfo by room.roomInfoFlow.collectAsState(null) + val roomInfo by room.roomInfoFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val canJoinCall by room.canCall(updateKey = syncUpdateFlow.value) val isUserInTheCall by remember { derivedStateOf { - room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty() + room.sessionId in roomInfo.activeRoomCallParticipants } } val currentCall by currentCallService.currentCall.collectAsState() @@ -41,7 +41,7 @@ class RoomCallStatePresenter @Inject constructor( } } val callState = when { - roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing( + roomInfo.hasRoomCall -> RoomCallState.OnGoing( canJoinCall = canJoinCall, isUserInTheCall = isUserInTheCall, isUserLocallyInTheCall = isUserLocallyInTheCall, diff --git a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt index 5555929456..23791eb444 100644 --- a/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt +++ b/features/roomcall/impl/src/test/kotlin/io/element/android/features/roomcall/impl/RoomCallStatePresenterTest.kt @@ -58,12 +58,10 @@ class RoomCallStatePresenterTest { fun `present - call is disabled if user cannot join it even if there is an ongoing call`() = runTest { val room = FakeMatrixRoom( canUserJoinCallResult = { Result.success(false) }, - ).apply { - givenRoomInfo(aRoomInfo(hasRoomCall = true)) - } + initialRoomInfo = aRoomInfo(hasRoomCall = true), + ) val presenter = createRoomCallStatePresenter(matrixRoom = room) presenter.test { - skipItems(1) assertThat(awaitItem()).isEqualTo( RoomCallState.OnGoing( canJoinCall = false, diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 1c092ec805..315b00b153 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -24,6 +24,7 @@ android { setupAnvil() dependencies { + implementation(projects.appconfig) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) @@ -51,6 +52,7 @@ dependencies { implementation(projects.features.messages.api) implementation(projects.features.roomcall.api) implementation(projects.features.knockrequests.api) + implementation(projects.features.verifysession.api) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) 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 774abfe8cd..b7cc8685e4 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 @@ -9,6 +9,7 @@ package io.element.android.features.roomdetails.impl import android.os.Parcelable import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -21,6 +22,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.Interaction import io.element.android.anvilannotations.ContributesNode +import io.element.android.appconfig.LearnMoreConfig import io.element.android.features.call.api.CallType import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint @@ -35,11 +37,13 @@ import io.element.android.features.roomdetails.impl.notificationsettings.RoomNot import io.element.android.features.roomdetails.impl.rolesandpermissions.RolesAndPermissionsFlowNode import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyFlowNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper +import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.operation.hide import io.element.android.libraries.architecture.overlay.operation.show +import io.element.android.libraries.designsystem.utils.OpenUrlInTabView import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId @@ -47,6 +51,7 @@ 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.room.MatrixRoom +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.services.analytics.api.AnalyticsService @@ -65,6 +70,7 @@ class RoomDetailsFlowNode @AssistedInject constructor( private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint, private val mediaViewerEntryPoint: MediaViewerEntryPoint, private val mediaGalleryEntryPoint: MediaGalleryEntryPoint, + private val verifySessionEntryPoint: VerifySessionEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), @@ -118,6 +124,9 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Parcelize data object SecurityAndPrivacy : NavTarget + + @Parcelize + data class VerifyUser(val userId: UserId) : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -168,6 +177,10 @@ class RoomDetailsFlowNode @AssistedInject constructor( backstack.push(NavTarget.SecurityAndPrivacy) } + override fun openDmUserProfile(userId: UserId) { + backstack.push(NavTarget.RoomMemberDetails(userId)) + } + override fun onJoinCall() { val inputs = CallType.RoomCall( sessionId = room.sessionId, @@ -224,6 +237,10 @@ class RoomDetailsFlowNode @AssistedInject constructor( override fun onStartCall(dmRoomId: RoomId) { elementCallEntryPoint.startCall(CallType.RoomCall(roomId = dmRoomId, sessionId = room.sessionId)) } + + override fun onVerifyUser(userId: UserId) { + backstack.push(NavTarget.VerifyUser(userId)) + } } val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback) createNode(buildContext, plugins) @@ -301,11 +318,37 @@ class RoomDetailsFlowNode @AssistedInject constructor( NavTarget.SecurityAndPrivacy -> { createNode(buildContext) } + is NavTarget.VerifyUser -> { + val params = VerifySessionEntryPoint.Params( + showDeviceVerifiedScreen = true, + verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId,) + ) + verifySessionEntryPoint.nodeBuilder(this, buildContext) + .params(params) + .callback(object : VerifySessionEntryPoint.Callback { + override fun onDone() { + backstack.pop() + } + + override fun onBack() { + backstack.pop() + } + + override fun onLearnMoreAboutEncryption() { + learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL + } + }) + .build() + } } } + private val learnMoreUrl = mutableStateOf(null) + @Composable override fun View(modifier: Modifier) { BackstackWithOverlayBox(modifier) + + OpenUrlInTabView(learnMoreUrl) } } 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 6ae3c55503..c3a0a3d519 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 @@ -23,6 +23,7 @@ import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.androidutils.system.startSharePlainTextIntent import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.CoroutineScope @@ -50,6 +51,7 @@ class RoomDetailsNode @AssistedInject constructor( fun openPinnedMessagesList() fun openKnockRequestsList() fun openSecurityAndPrivacy() + fun openDmUserProfile(userId: UserId) fun onJoinCall() } @@ -126,6 +128,10 @@ class RoomDetailsNode @AssistedInject constructor( callbacks.forEach { it.openSecurityAndPrivacy() } } + private fun onProfileClick(userId: UserId) { + callbacks.forEach { it.openDmUserProfile(userId) } + } + @Composable override fun View(modifier: Modifier) { val context = LocalContext.current @@ -158,7 +164,8 @@ class RoomDetailsNode @AssistedInject constructor( onJoinCallClick = ::onJoinCall, onPinnedMessagesClick = ::openPinnedMessages, onKnockRequestsClick = ::openKnockRequestsLists, - onSecurityAndPrivacyClick = ::openSecurityAndPrivacy + onSecurityAndPrivacyClick = ::openSecurityAndPrivacy, + onProfileClick = ::onProfileClick, ) } } 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 1bf44730e2..df1daf6386 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 @@ -25,17 +25,16 @@ import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.features.roomdetails.impl.securityandprivacy.permissions.securityAndPrivacyPermissionsAsState import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.StateEventType -import io.element.android.libraries.matrix.api.room.isDm import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canSendState @@ -43,7 +42,9 @@ import io.element.android.libraries.matrix.api.room.roomNotificationSettings import io.element.android.libraries.matrix.ui.room.canHandleKnockRequestsAsState import io.element.android.libraries.matrix.ui.room.getCurrentRoomMember import io.element.android.libraries.matrix.ui.room.getDirectRoomMember +import io.element.android.libraries.matrix.ui.room.isDmAsState import io.element.android.libraries.matrix.ui.room.isOwnUserAdmin +import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analyticsproviders.api.trackers.captureInteraction import kotlinx.collections.immutable.toPersistentList @@ -70,22 +71,22 @@ class RoomDetailsPresenter @Inject constructor( val scope = rememberCoroutineScope() val leaveRoomState = leaveRoomPresenter.present() val canShowNotificationSettings = remember { mutableStateOf(false) } - val roomInfo by room.roomInfoFlow.collectAsState(initial = null) + val roomInfo by room.roomInfoFlow.collectAsState() val isUserAdmin = room.isOwnUserAdmin() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val roomAvatar by remember { derivedStateOf { roomInfo?.avatarUrl ?: room.avatarUrl } } + val roomAvatar by remember { derivedStateOf { roomInfo.avatarUrl } } - val roomName by remember { derivedStateOf { (roomInfo?.name ?: room.displayName).trim() } } - val roomTopic by remember { derivedStateOf { roomInfo?.topic ?: room.topic } } - val isFavorite by remember { derivedStateOf { roomInfo?.isFavorite.orFalse() } } - val joinRule by remember { derivedStateOf { roomInfo?.joinRule } } + val roomName by remember { derivedStateOf { roomInfo.name?.trim().orEmpty() } } + val roomTopic by remember { derivedStateOf { roomInfo.topic } } + val isFavorite by remember { derivedStateOf { roomInfo.isFavorite } } + val joinRule by remember { derivedStateOf { roomInfo.joinRule } } val canShowPinnedMessages = isPinnedMessagesFeatureEnabled() var canShowMediaGallery by remember { mutableStateOf(false) } LaunchedEffect(Unit) { canShowMediaGallery = featureFlagService.isFeatureEnabled(FeatureFlags.MediaGallery) } - val pinnedMessagesCount by remember { derivedStateOf { roomInfo?.pinnedEventIds?.size } } + val pinnedMessagesCount by remember { derivedStateOf { roomInfo.pinnedEventIds.size } } LaunchedEffect(Unit) { canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings) @@ -98,6 +99,9 @@ class RoomDetailsPresenter @Inject constructor( val membersState by room.membersStateFlow.collectAsState() val canInvite by getCanInvite(membersState) + val canonicalAlias by remember { derivedStateOf { roomInfo.canonicalAlias } } + val isEncrypted by remember { derivedStateOf { roomInfo.isEncrypted == true } } + val isDm by room.isDmAsState() val canEditName by getCanSendState(membersState, StateEventType.ROOM_NAME) val canEditAvatar by getCanSendState(membersState, StateEventType.ROOM_AVATAR) val canEditTopic by getCanSendState(membersState, StateEventType.ROOM_TOPIC) @@ -106,6 +110,7 @@ class RoomDetailsPresenter @Inject constructor( val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) val roomType = getRoomType(dmMember, currentMember) val roomCallState = roomCallStatePresenter.present() + val joinedMemberCount by remember { derivedStateOf { roomInfo.joinedMembersCount } } val topicState = remember(canEditTopic, roomTopic, roomType) { val topic = roomTopic @@ -138,7 +143,7 @@ class RoomDetailsPresenter @Inject constructor( } RoomDetailsEvent.UnmuteNotification -> { scope.launch(dispatchers.io) { - client.notificationSettingsService().unmuteRoom(room.roomId, room.isEncrypted, room.isOneToOne) + client.notificationSettingsService().unmuteRoom(room.roomId, isEncrypted, room.isOneToOne) } } is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite) @@ -154,14 +159,20 @@ class RoomDetailsPresenter @Inject constructor( } } + val hasMemberVerificationViolations by produceState(false) { + room.roomMemberIdentityStateChange() + .onEach { identities -> value = identities.any { it.identityState == IdentityState.VerificationViolation } } + .launchIn(this) + } + return RoomDetailsState( roomId = room.roomId, roomName = roomName, - roomAlias = room.canonicalAlias, + roomAlias = canonicalAlias, roomAvatarUrl = roomAvatar, roomTopic = topicState, - memberCount = room.joinedMemberCount, - isEncrypted = room.isEncrypted, + memberCount = joinedMemberCount, + isEncrypted = isEncrypted, canInvite = canInvite, canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room, canShowNotificationSettings = canShowNotificationSettings.value, @@ -171,15 +182,16 @@ class RoomDetailsPresenter @Inject constructor( leaveRoomState = leaveRoomState, roomNotificationSettings = roomNotificationSettingsState.roomNotificationSettings(), isFavorite = isFavorite, - displayRolesAndPermissionsSettings = !room.isDm && isUserAdmin, + displayRolesAndPermissionsSettings = !isDm && isUserAdmin, isPublic = joinRule == JoinRule.Public, - heroes = roomInfo?.heroes.orEmpty().toPersistentList(), + heroes = roomInfo.heroes.toPersistentList(), canShowPinnedMessages = canShowPinnedMessages, canShowMediaGallery = canShowMediaGallery, pinnedMessagesCount = pinnedMessagesCount, canShowKnockRequests = canShowKnockRequests, knockRequestsCount = knockRequestsCount, canShowSecurityAndPrivacy = canShowSecurityAndPrivacy, + hasMemberVerificationViolations = hasMemberVerificationViolations, eventSink = ::handleEvents, ) } 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 8a9401d5f0..5502d4e29a 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 @@ -45,6 +45,7 @@ data class RoomDetailsState( val canShowKnockRequests: Boolean, val knockRequestsCount: Int?, val canShowSecurityAndPrivacy: Boolean, + val hasMemberVerificationViolations: Boolean, val eventSink: (RoomDetailsEvent) -> Unit ) { val roomBadges = buildList { 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 597f9517b9..b2db46115c 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 @@ -14,6 +14,7 @@ import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.RoomAlias @@ -50,6 +51,9 @@ open class RoomDetailsStateProvider : PreviewParameterProvider aRoomDetailsState(pinnedMessagesCount = 3), aRoomDetailsState(knockRequestsCount = null, canShowKnockRequests = true), aRoomDetailsState(knockRequestsCount = 4, canShowKnockRequests = true), + aRoomDetailsState(hasMemberVerificationViolations = true), + aDmRoomDetailsState(dmRoomMemberVerificationState = UserProfileVerificationState.VERIFIED), + aDmRoomDetailsState(dmRoomMemberVerificationState = UserProfileVerificationState.VERIFICATION_VIOLATION), // Add other state here ) } @@ -110,6 +114,7 @@ fun aRoomDetailsState( canShowKnockRequests: Boolean = false, knockRequestsCount: Int? = null, canShowSecurityAndPrivacy: Boolean = true, + hasMemberVerificationViolations: Boolean = false, eventSink: (RoomDetailsEvent) -> Unit = {}, ) = RoomDetailsState( roomId = roomId, @@ -137,6 +142,7 @@ fun aRoomDetailsState( canShowKnockRequests = canShowKnockRequests, knockRequestsCount = knockRequestsCount, canShowSecurityAndPrivacy = canShowSecurityAndPrivacy, + hasMemberVerificationViolations = hasMemberVerificationViolations, eventSink = eventSink ) @@ -152,6 +158,7 @@ fun aDmRoomDetailsState( isDmMemberIgnored: Boolean = false, roomName: String = "Daniel", isEncrypted: Boolean = true, + dmRoomMemberVerificationState: UserProfileVerificationState = UserProfileVerificationState.UNKNOWN, ) = aRoomDetailsState( roomName = roomName, isPublic = false, @@ -162,5 +169,6 @@ fun aDmRoomDetailsState( ), roomMemberDetailsState = aUserProfileState( isBlocked = AsyncData.Success(isDmMemberIgnored), + verificationState = dmRoomMemberVerificationState, ) ) 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 c1e6a84617..6e1ddef204 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 @@ -39,6 +39,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.roomcall.api.hasPermissionToJoin +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage @@ -70,6 +71,7 @@ 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.RoomAlias 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.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.getBestName @@ -101,6 +103,7 @@ fun RoomDetailsView( onPinnedMessagesClick: () -> Unit, onKnockRequestsClick: () -> Unit, onSecurityAndPrivacyClick: () -> Unit, + onProfileClick: (UserId) -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -179,17 +182,26 @@ fun RoomDetailsView( state.eventSink(RoomDetailsEvent.SetFavorite(it)) } ) + if (state.canShowSecurityAndPrivacy) { SecurityAndPrivacyItem( onClick = onSecurityAndPrivacyClick ) } + + state.roomMemberDetailsState?.let { dmMemberDetails -> + ProfileItem( + verificationState = dmMemberDetails.verificationState, + onClick = { onProfileClick(dmMemberDetails.userId) } + ) + } } if (state.roomType is RoomDetailsType.Room) { PreferenceCategory { MembersItem( memberCount = state.memberCount, + hasVerificationViolations = state.hasMemberVerificationViolations, openRoomMemberList = openRoomMemberList, ) if (state.canShowKnockRequests) { @@ -546,15 +558,46 @@ private fun FavoriteItem( ) } +@Composable +private fun ProfileItem( + verificationState: UserProfileVerificationState, + onClick: () -> Unit, +) { + ListItem( + leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile())), + headlineContent = { Text(stringResource(id = R.string.screen_room_details_profile_row_title)) }, + trailingContent = when (verificationState) { + UserProfileVerificationState.VERIFIED -> ListItemContent.Icon( + iconSource = IconSource.Vector(CompoundIcons.Verified()), + tintColor = ElementTheme.colors.iconSuccessPrimary, + ) + UserProfileVerificationState.VERIFICATION_VIOLATION -> ListItemContent.Icon( + iconSource = IconSource.Vector(CompoundIcons.ErrorSolid()), + tintColor = ElementTheme.colors.iconCriticalPrimary, + ) + else -> null + }, + onClick = onClick, + ) +} + @Composable private fun MembersItem( memberCount: Long, + hasVerificationViolations: Boolean, openRoomMemberList: () -> Unit, ) { ListItem( headlineContent = { Text(stringResource(CommonStrings.common_people)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.User())), - trailingContent = ListItemContent.Text(memberCount.toString()), + trailingContent = if (hasVerificationViolations) { + ListItemContent.Icon( + iconSource = IconSource.Vector(CompoundIcons.ErrorSolid()), + tintColor = ElementTheme.colors.textCriticalPrimary, + ) + } else { + ListItemContent.Text(memberCount.toString()) + }, onClick = openRoomMemberList, ) } @@ -655,5 +698,6 @@ private fun ContentToPreview(state: RoomDetailsState) { onPinnedMessagesClick = {}, onKnockRequestsClick = {}, onSecurityAndPrivacyClick = {}, + onProfileClick = {}, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt index e6473eb4c3..4001eb7edb 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/di/RoomMemberModule.kt @@ -14,6 +14,7 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe import io.element.android.features.userprofile.api.UserProfilePresenterFactory import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.MatrixRoom @Module @@ -23,6 +24,7 @@ object RoomMemberModule { fun provideRoomMemberDetailsPresenterFactory( room: MatrixRoom, userProfilePresenterFactory: UserProfilePresenterFactory, + encryptionService: EncryptionService, ): RoomMemberDetailsPresenter.Factory { return object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { @@ -30,6 +32,7 @@ object RoomMemberModule { roomMemberId = roomMemberId, room = room, userProfilePresenterFactory = userProfilePresenterFactory, + encryptionService = encryptionService, ) } } 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 4e9ceb5bf3..e154c7ea15 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 @@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -24,13 +25,23 @@ 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.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.core.UserId +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.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.ui.room.canInviteAsState +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.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext class RoomMemberListPresenter @AssistedInject constructor( @@ -38,6 +49,7 @@ class RoomMemberListPresenter @AssistedInject constructor( private val roomMemberListDataSource: RoomMemberListDataSource, private val coroutineDispatchers: CoroutineDispatchers, private val roomMembersModerationPresenter: Presenter, + private val encryptionService: EncryptionService, @Assisted private val navigator: RoomMemberListNavigator, ) : Presenter { @AssistedFactory @@ -60,12 +72,20 @@ class RoomMemberListPresenter @AssistedInject constructor( val roomModerationState = roomMembersModerationPresenter.present() + val roomMemberIdentityStates by produceState(persistentMapOf()) { + room.roomMemberIdentityStateChange() + .onEach { identities -> + value = identities.associateBy({ it.identityRoomMember.userId }, { it.identityState }).toPersistentMap() + } + .launchIn(this) + } + // Ensure we load the latest data when entering this screen LaunchedEffect(Unit) { room.updateMembers() } - LaunchedEffect(membersState) { + LaunchedEffect(membersState, roomMemberIdentityStates) { if (membersState is MatrixRoomMembersState.Unknown) { return@LaunchedEffect } @@ -84,11 +104,17 @@ class RoomMemberListPresenter @AssistedInject constructor( return@withContext } val result = RoomMembers( - invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(), + invited = members.getOrDefault(RoomMembershipState.INVITE, emptyList()) + .map { it.withIdentityState(roomMemberIdentityStates) } + .toImmutableList(), joined = members.getOrDefault(RoomMembershipState.JOIN, emptyList()) .sortedWith(PowerLevelRoomMemberComparator()) + .map { it.withIdentityState(roomMemberIdentityStates) } + .toImmutableList(), + banned = members.getOrDefault(RoomMembershipState.BAN, emptyList()) + .sortedBy { it.userId.value } + .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), - banned = members.getOrDefault(RoomMembershipState.BAN, emptyList()).sortedBy { it.userId.value }.toImmutableList(), ) roomMembers = if (membersState is MatrixRoomMembersState.Pending) { AsyncData.Loading(result) @@ -108,11 +134,17 @@ class RoomMemberListPresenter @AssistedInject constructor( SearchBarResultState.NoResultsFound() } else { val result = RoomMembers( - invited = results.getOrDefault(RoomMembershipState.INVITE, emptyList()).toImmutableList(), + invited = results.getOrDefault(RoomMembershipState.INVITE, emptyList()) + .map { it.withIdentityState(roomMemberIdentityStates) } + .toImmutableList(), joined = results.getOrDefault(RoomMembershipState.JOIN, emptyList()) .sortedWith(PowerLevelRoomMemberComparator()) + .map { it.withIdentityState(roomMemberIdentityStates) } + .toImmutableList(), + banned = results.getOrDefault(RoomMembershipState.BAN, emptyList()) + .sortedBy { it.userId.value } + .map { it.withIdentityState(roomMemberIdentityStates) } .toImmutableList(), - banned = results.getOrDefault(RoomMembershipState.BAN, emptyList()).sortedBy { it.userId.value }.toImmutableList(), ) SearchBarResultState.Results( if (membersState is MatrixRoomMembersState.Pending) { @@ -149,4 +181,13 @@ class RoomMemberListPresenter @AssistedInject constructor( eventSink = { handleEvents(it) }, ) } + + private suspend fun RoomMember.withIdentityState(identityStates: ImmutableMap): RoomMemberWithIdentityState { + return if (room.info().isEncrypted != true) { + RoomMemberWithIdentityState(this, null) + } else { + val identityState = identityStates[userId] ?: encryptionService.getUserIdentity(userId).getOrNull() + RoomMemberWithIdentityState(this, identityState) + } + } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt index c552dda52e..b5fa6c37a9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListState.kt @@ -10,6 +10,7 @@ package io.element.android.features.roomdetails.impl.members import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.theme.components.SearchBarResultState +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.collections.immutable.ImmutableList @@ -24,7 +25,12 @@ data class RoomMemberListState( ) data class RoomMembers( - val invited: ImmutableList, - val joined: ImmutableList, - val banned: ImmutableList, + val invited: ImmutableList, + val joined: ImmutableList, + val banned: ImmutableList, +) + +data class RoomMemberWithIdentityState( + val roomMember: RoomMember, + val identityState: IdentityState?, ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt index f917000154..6fec01c675 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListStateProvider.kt @@ -13,6 +13,7 @@ import io.element.android.features.roomdetails.impl.members.moderation.aRoomMemb import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState import kotlinx.collections.immutable.persistentListOf @@ -23,8 +24,21 @@ internal class RoomMemberListStateProvider : PreviewParameterProvider String)?, - members: ImmutableList?, + members: ImmutableList?, onMemberSelected: (RoomMember) -> Unit, ) { headerText?.let { @@ -284,34 +290,63 @@ private fun LazyListScope.roomMemberListSection( items(members.orEmpty()) { matrixUser -> RoomMemberListItem( modifier = Modifier.fillMaxWidth(), - roomMember = matrixUser, - onClick = { onMemberSelected(matrixUser) } + roomMemberWithIdentity = matrixUser, + onClick = { onMemberSelected(matrixUser.roomMember) } ) } } @Composable private fun RoomMemberListItem( - roomMember: RoomMember, + roomMemberWithIdentity: RoomMemberWithIdentityState, onClick: () -> Unit, modifier: Modifier = Modifier, ) { - val roleText = when (roomMember.role) { + val roleText = when (roomMemberWithIdentity.roomMember.role) { RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_member_list_role_administrator) RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_member_list_role_moderator) RoomMember.Role.USER -> null } + MatrixUserRow( modifier = modifier.clickable(onClick = onClick), - matrixUser = roomMember.toMatrixUser(), + matrixUser = roomMemberWithIdentity.roomMember.toMatrixUser(), avatarSize = AvatarSize.UserListItem, - trailingContent = roleText?.let { - @Composable { - Text( - text = it, - style = ElementTheme.typography.fontBodySmRegular, - color = ElementTheme.colors.textSecondary, - ) + trailingContent = { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + when (roomMemberWithIdentity.identityState) { + IdentityState.Verified -> { + Icon( + modifier = Modifier.size(20.dp), + imageVector = CompoundIcons.Verified(), + contentDescription = stringResource(CommonStrings.common_verified), + tint = ElementTheme.colors.iconSuccessPrimary + ) + } + IdentityState.VerificationViolation -> { + Icon( + modifier = Modifier.size(20.dp), + imageVector = CompoundIcons.ErrorSolid(), + contentDescription = stringResource( + CommonStrings.crypto_identity_change_profile_pin_violation, + roomMemberWithIdentity.roomMember.getBestName() + ), + tint = ElementTheme.colors.iconCriticalPrimary + ) + } + else -> Unit + } + + roleText?.let { + Text( + text = it, + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + } } } ) 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 7b5ce77f2b..6a1af42694 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 @@ -37,7 +37,7 @@ class RoomMemberDetailsNode @AssistedInject constructor( presenterFactory: RoomMemberDetailsPresenter.Factory, ) : Node(buildContext, plugins = plugins) { data class RoomMemberDetailsInput( - val roomMemberId: UserId + val roomMemberId: UserId, ) : NodeInputs private val inputs = inputs() @@ -79,6 +79,7 @@ class RoomMemberDetailsNode @AssistedInject constructor( onOpenDm = ::onStartDM, onStartCall = ::onStartCall, openAvatarPreview = callback::openAvatarPreview, + onVerifyClick = callback::onVerifyUser, ) } } 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 b2ec81b0c0..9a5d456cbe 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 @@ -11,14 +11,28 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import dagger.assisted.Assisted import dagger.assisted.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 +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId +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.encryption.identity.IdentityStateChange import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState +import io.element.android.libraries.matrix.ui.room.roomMemberIdentityStateChange +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch /** * Presenter for room member details screen. @@ -27,6 +41,7 @@ import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState class RoomMemberDetailsPresenter @AssistedInject constructor( @Assisted private val roomMemberId: UserId, private val room: MatrixRoom, + private val encryptionService: EncryptionService, userProfilePresenterFactory: UserProfilePresenterFactory, ) : Presenter { interface Factory { @@ -35,8 +50,11 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( private val userProfilePresenter = userProfilePresenterFactory.create(roomMemberId) + @OptIn(ExperimentalCoroutinesApi::class) @Composable override fun present(): UserProfileState { + val coroutineScope = rememberCoroutineScope() + val roomMember by room.getRoomMemberAsState(roomMemberId) LaunchedEffect(Unit) { // Update room member info when opening this screen @@ -60,9 +78,54 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( val userProfileState = userProfilePresenter.present() + val identityStateChanges by produceState(initialValue = null) { + room.roomInfoFlow.filter { it.isEncrypted == true } + .flatMapLatest { + // Fetch the initial identity state manually + val identityState = encryptionService.getUserIdentity(roomMemberId).getOrNull() + value = identityState?.let { IdentityStateChange(roomMemberId, it) } + + // Subscribe to the identity changes + room.roomMemberIdentityStateChange() + .map { it.find { it.identityRoomMember.userId == roomMemberId } } + .map { roomMemberIdentityStateChange -> + // If we didn't receive any info, manually fetch it + roomMemberIdentityStateChange?.identityState ?: encryptionService.getUserIdentity(roomMemberId).getOrNull() + } + .filterNotNull() + } + .collect { value = IdentityStateChange(roomMemberId, it) } + } + + val verificationState = remember(identityStateChanges) { + when (identityStateChanges?.identityState) { + IdentityState.VerificationViolation -> UserProfileVerificationState.VERIFICATION_VIOLATION + IdentityState.Verified -> UserProfileVerificationState.VERIFIED + IdentityState.Pinned, IdentityState.PinViolation -> UserProfileVerificationState.UNVERIFIED + else -> UserProfileVerificationState.UNKNOWN + } + } + + fun eventSink(event: UserProfileEvents) { + when (event) { + UserProfileEvents.WithdrawVerification -> coroutineScope.launch { + encryptionService.withdrawVerification(roomMemberId) + } + else -> Unit + } + } + return userProfileState.copy( userName = roomUserName ?: userProfileState.userName, avatarUrl = roomUserAvatar ?: userProfileState.avatarUrl, + verificationState = verificationState, + eventSink = { event -> + if (event is UserProfileEvents.WithdrawVerification) { + eventSink(UserProfileEvents.WithdrawVerification) + } else { + userProfileState.eventSink(event) + } + } ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt index cbd9d3aca0..e9d4a91514 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenter.kt @@ -50,7 +50,7 @@ class RoomMembersModerationPresenter @Inject constructor( val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val canBan by room.canBanAsState(syncUpdateFlow.value) val canKick by room.canKickAsState(syncUpdateFlow.value) - val isDm by room.isDmAsState(syncUpdateFlow.value) + val isDm by room.isDmAsState() val currentUserMemberPowerLevel by room.userPowerLevelAsState(syncUpdateFlow.value) val canDisplayModerationActions by remember { 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 77bdafe94a..7dae51a1a2 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 @@ -12,6 +12,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable @@ -78,11 +79,23 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( mutableStateOf(null) } + val displayName by produceState(room.info().name) { + room.roomInfoFlow.collect { value = it.name } + } + + val isRoomEncrypted by produceState(room.info().isEncrypted) { + room.roomInfoFlow.collect { value = it.isEncrypted } + } + LaunchedEffect(Unit) { getDefaultRoomNotificationMode(defaultRoomNotificationMode) fetchNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings) observeNotificationSettings(pendingRoomNotificationMode, roomNotificationSettings) - shouldDisplayMentionsOnlyDisclaimer = room.isEncrypted && !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) + } + + LaunchedEffect(isRoomEncrypted) { + shouldDisplayMentionsOnlyDisclaimer = isRoomEncrypted == true && + !notificationSettingsService.canHomeServerPushEncryptedEventsToDevice().getOrDefault(true) } fun handleEvents(event: RoomNotificationSettingsEvents) { @@ -113,7 +126,7 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( return RoomNotificationSettingsState( showUserDefinedSettingStyle = showUserDefinedSettingStyle, - roomName = room.displayName, + roomName = displayName.orEmpty(), roomNotificationSettings = roomNotificationSettings.value, pendingRoomNotificationMode = pendingRoomNotificationMode.value, pendingSetDefault = pendingSetDefault.value, @@ -143,16 +156,18 @@ class RoomNotificationSettingsPresenter @AssistedInject constructor( roomNotificationSettings: MutableState> ) = launch { suspend { + val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow() pendingModeState.value = null - notificationSettingsService.getRoomNotificationSettings(room.roomId, room.isEncrypted, room.isOneToOne).getOrThrow() + notificationSettingsService.getRoomNotificationSettings(room.roomId, isEncrypted, room.isOneToOne).getOrThrow() }.runCatchingUpdatingState(roomNotificationSettings) } private fun CoroutineScope.getDefaultRoomNotificationMode( defaultRoomNotificationMode: MutableState ) = launch { + val isEncrypted = room.info().isEncrypted ?: room.getUpdatedIsEncrypted().getOrThrow() defaultRoomNotificationMode.value = notificationSettingsService.getDefaultRoomNotificationMode( - room.isEncrypted, + isEncrypted, room.isOneToOne ).getOrThrow() } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt index 5cd4bd1737..37e72d158f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt @@ -39,7 +39,7 @@ class RolesAndPermissionsPresenter @Inject constructor( @Composable override fun present(): RolesAndPermissionsState { val coroutineScope = rememberCoroutineScope() - val roomInfo by room.roomInfoFlow.collectAsState(initial = null) + val roomInfo by room.roomInfoFlow.collectAsState() val roomMembers by room.membersStateFlow.collectAsState() // Get the list of active room members (joined or invited), in order to filter members present in the power // level state Event. @@ -109,8 +109,8 @@ class RolesAndPermissionsPresenter @Inject constructor( } } - private fun MatrixRoomInfo?.userCountWithRole(userIds: List, role: RoomMember.Role): Int { - return this?.userPowerLevels.orEmpty().count { (userId, level) -> + private fun MatrixRoomInfo.userCountWithRole(userIds: List, role: RoomMember.Role): Int { + return this.userPowerLevels.count { (userId, level) -> RoomMember.Role.forPowerLevel(level) == role && userId in userIds } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt index 392f9da096..8878dc8876 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt @@ -106,10 +106,10 @@ class ChangeRolesPresenter @AssistedInject constructor( val hasPendingChanges = usersWithRole.value != selectedUsers.value - val roomInfo by room.roomInfoFlow.collectAsState(initial = null) + val roomInfo by room.roomInfoFlow.collectAsState() fun canChangeMemberRole(userId: UserId): Boolean { // An admin can't remove or demote another admin - val powerLevel = roomInfo?.userPowerLevels?.get(userId) ?: 0L + val powerLevel = roomInfo.userPowerLevels[userId] ?: 0L return RoomMember.Role.forPowerLevel(powerLevel) != RoomMember.Role.ADMIN } 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 769ff87bae..d6f27a9a5b 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 @@ -57,7 +57,7 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor( val saveAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val homeserverName = remember { matrixClient.userIdServerName() } val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val roomInfo = room.roomInfoFlow.collectAsState(null) + val roomInfo by room.roomInfoFlow.collectAsState() val savedIsVisibleInRoomDirectory = remember { mutableStateOf>(AsyncData.Uninitialized) } LaunchedEffect(Unit) { @@ -66,12 +66,13 @@ class SecurityAndPrivacyPresenter @AssistedInject constructor( val savedSettings by remember { derivedStateOf { + val historyVisibility = roomInfo.historyVisibility.map() SecurityAndPrivacySettings( - roomAccess = roomInfo.value?.joinRule.map(), - isEncrypted = room.isEncrypted, + roomAccess = roomInfo.joinRule.map(), + isEncrypted = roomInfo.isEncrypted == true, isVisibleInRoomDirectory = savedIsVisibleInRoomDirectory.value, - historyVisibility = roomInfo.value?.historyVisibility.map(), - address = roomInfo.value?.firstDisplayableAlias(homeserverName)?.value, + historyVisibility = historyVisibility, + address = roomInfo.firstDisplayableAlias(homeserverName)?.value, ) } } 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 c494443f1b..2c581e4584 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 @@ -9,6 +9,8 @@ package io.element.android.features.roomdetails.impl.securityandprivacy.editroom import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -24,6 +26,7 @@ import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper import io.element.android.libraries.matrix.api.roomAliasFromName import io.element.android.libraries.matrix.ui.room.address.RoomAddressValidity @@ -45,15 +48,16 @@ class EditRoomAddressPresenter @AssistedInject constructor( @Composable override fun present(): EditRoomAddressState { val coroutineScope = rememberCoroutineScope() + val roomInfo by room.roomInfoFlow.collectAsState() val homeserverName = remember { client.userIdServerName() } val roomAddressValidity = remember { mutableStateOf(RoomAddressValidity.Unknown) } - val savedRoomAddress = remember { room.firstAliasMatching(homeserverName)?.addressName() } + val savedRoomAddress by remember { derivedStateOf { roomInfo.firstAliasMatching(homeserverName)?.addressName() } } val saveAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } var newRoomAddress by remember { mutableStateOf( - savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(room.displayName) + savedRoomAddress ?: roomAliasHelper.roomAliasNameFromRoomDisplayName(roomInfo.name.orEmpty()) ) } @@ -97,8 +101,9 @@ class EditRoomAddressPresenter @AssistedInject constructor( newRoomAddress: String, ) = launch { suspend { - val savedCanonicalAlias = room.canonicalAlias - val savedAliasFromHomeserver = room.firstAliasMatching(serverName) + val roomInfo = room.info() + val savedCanonicalAlias = roomInfo.canonicalAlias + val savedAliasFromHomeserver = roomInfo.firstAliasMatching(serverName) val newRoomAlias = client.roomAliasFromName(newRoomAddress) ?: throw IllegalArgumentException("Invalid room address") // First publish the new alias in the room directory @@ -112,7 +117,7 @@ class EditRoomAddressPresenter @AssistedInject constructor( when { // Allow to update the canonical alias only if the saved canonical alias matches the homeserver or if there is no canonical alias savedCanonicalAlias == null || savedCanonicalAlias.matchesServer(serverName) -> { - val newAlternativeAliases = room.alternativeAliases.filter { it != savedAliasFromHomeserver } + val newAlternativeAliases = roomInfo.alternativeAliases.filter { it != savedAliasFromHomeserver } room.updateCanonicalAlias(newRoomAlias, newAlternativeAliases).getOrThrow() } // Otherwise, only update the alternative aliases and keep the current canonical alias @@ -121,7 +126,7 @@ class EditRoomAddressPresenter @AssistedInject constructor( // New alias is added first, so we make sure we pick it first add(newRoomAlias) // Add all other aliases, except the one we just removed from the room directory - addAll(room.alternativeAliases.filter { it != savedAliasFromHomeserver }) + addAll(roomInfo.alternativeAliases.filter { it != savedAliasFromHomeserver }) } room.updateCanonicalAlias(savedCanonicalAlias, newAlternativeAliases).getOrThrow() } @@ -134,7 +139,7 @@ class EditRoomAddressPresenter @AssistedInject constructor( /** * Returns the first alias that matches the given server name, or null if none match. */ -private fun MatrixRoom.firstAliasMatching(serverName: String): RoomAlias? { +private fun MatrixRoomInfo.firstAliasMatching(serverName: String): RoomAlias? { // Check if the canonical alias matches the homeserver if (canonicalAlias?.matchesServer(serverName) == true) { return canonicalAlias diff --git a/features/roomdetails/impl/src/main/res/values-es/translations.xml b/features/roomdetails/impl/src/main/res/values-es/translations.xml index de509e7e81..42e3ec8315 100644 --- a/features/roomdetails/impl/src/main/res/values-es/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-es/translations.xml @@ -1,17 +1,19 @@ + "Necesitarás una dirección de sala para que sea visible en el directorio." + "Dirección de la sala" "Se ha producido un error al actualizar la configuración de notificaciones." "Tu servidor principal no admite esta opción en salas cifradas, puede que no recibas notificaciones en algunas salas." "Encuestas" "Solo administradores" - "Prohibir personas" + "Vetar personas" "Eliminar mensajes" "Todos" - "Invitar a personas" + "Invitar personas y aceptar solicitudes de unión" "Moderación de miembros" "Mensajes y contenido" "Administradores y moderadores" - "Eliminar personas" + "Eliminar personas y rechazar solicitudes de unión" "Cambiar el avatar de la sala" "Detalles de la sala" "Cambiar el nombre de la sala" @@ -23,6 +25,9 @@ "Degradar" "No podrás deshacer este cambio ya que te estás degradando. Si eres el último usuario privilegiado en la sala será imposible recuperar los privilegios." "¿Degradarte?" + "%1$s (Pendiente)" + "(Pendiente)" + "Los administradores tienen privilegios de moderador de forma automática" "Editar moderadores" "Administradores" "Moderadores" @@ -32,6 +37,9 @@ "Añadir tema" "Ya eres miembro" "Ya estás invitado" + "Cifrada" + "No cifrada" + "Sala pública" "Editar sala" "Se ha producido un error desconocido y no se ha podido cambiar la información." "No se puede actualizar la sala" @@ -43,41 +51,47 @@ "Invitar personas" "Salir de la conversación" "Salir de la sala" + "Medios y archivos" "Personalizado" "Por defecto" "Notificaciones" + "Mensajes fijados" + "Perfil" + "Solicitudes de unión" "Roles y permisos" "Nombre de la sala" + "Seguridad y privacidad" "Seguridad" "Compartir sala" + "Información de la sala" "Tema" "Actualizando la sala…" - "Prohibir" + "Vetar" "No podrán volver a unirse a esta sala si son invitados." - "¿Estás seguro de que quieres prohibir a este miembro?" - "No hay usuarios prohibidos en esta sala." - "Prohibiendo %1$s" + "¿Estás seguro de que quieres vetar a este miembro?" + "No hay usuarios vetados en esta sala." + "Vetando a %1$s" "Una persona" "%1$d personas" - "Eliminar y prohibir a un miembro" - "Remover de la sala" - "Eliminar y prohibir a un miembro" + "Sacar y vetar a un miembro" + "Sacar de la sala" + "Sacar y vetar a un miembro" "Solo eliminar miembro" - "¿Eliminar al miembro y prohibirle unirse en el futuro?" - "Anular la prohibición" + "¿Sacar al miembro y prohibirle unirse en el futuro?" + "Quitar veto" "Podrán volver a unirse a esta sala si son invitados de nuevo." - "Desprohibir al usuario" + "Quitar veto al usuario" "Ver perfil" - "Prohibidos" + "Vetados" "Miembros" "Pendiente" "Eliminando %1$s…" "Admin" "Moderador" "Miembros de la sala" - "Dejando de prohibir %1$s" + "Levantando veto a %1$s" "Permitir configuración personalizada" "Si activas esta opción, anularás tu configuración por defecto" "Notificarme en este chat para" @@ -106,4 +120,37 @@ "Roles" "Detalles de la sala" "Roles y permisos" + "Agregar dirección de sala" + "Cualquiera puede solicitar unirse a la sala, pero un administrador o moderador tendrá que aceptar la solicitud." + "Solicitud para unirse" + "Sí, activar cifrado" + "Una vez activado, el cifrado de una sala no se puede desactivar. El historial de mensajes solo será visible para los miembros de la sala desde que fueron invitados o desde que se unieron a la sala. +Nadie más que los miembros de la sala podrán leer los mensajes. Esto puede impedir que los bots y los puentes funcionen correctamente. +No recomendamos habilitar el cifrado para las salas que cualquiera pueda encontrar y unirse." + "¿Activar cifrado?" + "Una vez activado, el cifrado no se puede desactivar." + "Cifrado" + "Activar el cifrado de extremo a extremo" + "Cualquiera puede encontrarla y unirse" + "Cualquiera" + "Las personas solo pueden unirse si están invitadas" + "Solo por invitación" + "Acceso a la sala" + "No se admiten los espacios por el momento." + "Miembros del espacio" + "Necesitarás una dirección de sala para que esta sea visible en el directorio de salas." + "Dirección de la sala" + "Permite encontrar esta sala buscando en el directorio de salas públicas de %1$s" + "Visible en el directorio de salas públicas" + "Cualquiera" + "Quién puede leer el historial" + "Solo participantes desde que fueron invitados" + "Solo participantes desde que se selecciona esta opción" + "Las direcciones de sala son formas de buscar salas y acceder a ellas. Esto también garantiza que puedas compartir fácilmente tu sala con otras personas. +Puedes optar por publicar tu sala en el directorio de salas públicas de tu servidor base." + "Publicación de la sala" + "Las direcciones de sala son un medio para buscar y acceder a salas. También te permiten compartir fácilmente tu sala con otras personas. +La dirección también es necesaria para hacer visible la sala en el directorio de salas públicas de %1$s." + "Visibilidad de la sala" + "Seguridad y privacidad" diff --git a/features/roomdetails/impl/src/main/res/values-eu/translations.xml b/features/roomdetails/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..8968f36d80 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,128 @@ + + + "Gelaren helbidea" + "Errorea gertatu da jakinarazpen-ezarpena eguneratzean." + "Zure zerbitzaria ez da bateragarria enkriptatutako gelen aukerarekin; litekeena da gela batzuetako jakinarazpenak ez jasotzea." + "Inkestak" + "Administratzaileak soilik" + "Jarri debekua jendeari" + "Kendu mezuak" + "Guztiak" + "Gonbidatu jendea" + "Kideen moderazioa" + "Mezuak eta edukiak" + "Administratzaileak eta moderatzaileak" + "Kendu jendea" + "Aldatu gelaren abatarra" + "Gelaren xehetasunak" + "Aldatu gelaren izena" + "Aldatu gelako mintzagaia" + "Bidali mezuak" + "Editatu administratzaileak" + "Administratzailea gehitu?" + "Jaitsi mailaz" + "Ezin izango duzu hau aldatu zure burua mailaz jaisten ari zarelako, zu bazara gelan baimenak dituen azken erabiltzailea ezin izango dira baimenak berreskuratu." + "Zure burua mailaz jaitsi?" + "%1$s (zain)" + "(Egiteke)" + "Administratzaileek automatikoki dute moderatzaile-pribilegioak" + "Editatu moderatzaileak" + "Administratzaileak" + "Moderatzaileak" + "Kideak" + "Gorde gabeko aldaketak dituzu." + "Aldaketak gorde?" + "Gehitu hizketagaia" + "Kidea da dagoeneko" + "Lehendik ere gonbidatuta" + "Zifratuta" + "Zifratu gabe" + "Gela publikoa" + "Editatu gela" + "Errore ezezaguna gertatu da eta ezin izan da informazioa aldatu." + "Ezin da gela eguneratu" + "Mezuen enkriptazioa gaituta dago" + "Errorea gertatu da jakinarazpen-ezarpenak kargatzean." + "Ezin izan da gela mututu; saiatu berriro." + "Ezin izan da gela mututzeari utzi; saiatu berriro." + "Gonbidatu jendea" + "Utzi elkarrizketa" + "Atera gelatik" + "Multimedia eta fitxategiak" + "Lehenetsia" + "Jakinarazpenak" + "Finkatutako mezuak" + "Sartzeko eskaerak" + "Rolak eta baimenak" + "Gelaren izena" + "Segurtasuna eta pribatutasuna" + "Segurtasuna" + "Partekatu gela" + "Gelaren informazioa" + "Gaia" + "Gela eguneratzen…" + "Ezarri debekua" + "Ziur kide honi debekua ezarri nahi diozula?" + "Gela honetan ez dago debekua ezarri zaion erabiltzailerik." + "%1$s(r)i debekua ezartzen" + + "Pertsona %1$d" + "%1$d pertsona" + + "Kendu kidea eta ezarri debekua" + "Kendu gelatik" + "Kendu kidea eta ezarri debekua" + "Kendu kidea soilik" + "Kidea kendu eta etorkizunean sartzea debekatu?" + "Kendu debekua" + "Kendu debekua erabiltzaileari" + "Ikusi profila" + "Debekatuta" + "Kideak" + "Zain" + "%1$s kentzen…" + "Kudeatzailea" + "Moderatzailea" + "Gelako kideak" + "%1$s(r)i debekua kentzen" + "Aktibatuz gero, defektuzko ezarpena gainidatziko du" + "Jakinarazi txat honetan" + "%1$s alda dezakezu." + "Ezarpen orokorretan" + "Defektuzko ezarpena" + "Errorea gertatu da jakinarazpen-ezarpenak kargatzean." + "Zure zerbitzaria ez da bateragarria enkriptatutako gelen aukerarekin; litekeena da gela batzuetako jakinarazpenak ez jasotzea." + "Mezu guztiak" + "Aipamenak eta hitz gakoak soilik" + "Gela honetan, jakinarazi" + "Administratzaileak" + "Aldatu nire rola" + "Jaitsi maila, kidera" + "Jaitsi maila, moderatzailera" + "Kideen moderazioa" + "Mezuak eta edukiak" + "Moderatzaileak" + "Baimenak" + "Berrezarri baimenak" + "Baimenak berrezarritakoan, uneko ezarpenak galduko dituzu." + "Baimenak berrezarri?" + "Rolak" + "Gelaren xehetasunak" + "Rolak eta baimenak" + "Gehitu gelaren helbidea" + "Zifratzea" + "Edonork aurkitu eta bat egin dezake" + "Edonork" + "Gelarako sarbidea" + "Gaur-gaurkoz ez da guneekin bateragarria" + "Guneko kideak" + "Gelarako helbideren bat beharko duzu gelen direktorioan ikusgai egon dadila nahi baduzu." + "Gelaren helbidea" + "Gela publikoen direktorioan ikusgai" + "Edonork" + "Nork irakur dezake historia" + "Kideek bakarrik, gonbidatu zituztenetik" + "Kideek bakarrik, aukera hau hautatu zenetik" + "Gelaren ikusgarritasuna" + "Segurtasuna eta pribatutasuna" + 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 65ce26797e..c4a019ec9e 100644 --- a/features/roomdetails/impl/src/main/res/values-ka/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ka/translations.xml @@ -3,7 +3,34 @@ "შეტყობინებების პარამეტრის განახლებისას მოხდა შეცდომა." "თქვენი სახლის სერვერი არ უჭერს მხარს ამ პარამეტრს დაშიფრულ ოთახებში, ზოგიერთ ოთახში შეიძლება არ მიიღოთ შეტყობინება." "გამოკითხვები" + "მხოლოდ ადმინისტრატორები" + "მომხმარებლების დაბლოკვა" + "შეტყობინებების წაშლა" "ყველა" + "მომხმარებლების მოწვევა და გაწევრიანების მოთხოვნების დადასტურება" + "წევრების მოდერირება" + "შეტყობინებები და შინაარსი" + "ადმინისტრატორები და მოდერატორები" + "მომხმარებლების გაგდება და გაწევრიანების მოთხოვნების უარყოფა" + "ოთახის სურათის შეცვლა" + "ოთახის დეტალები" + "ოთახის სახელის შეცვლა" + "ოთახის თემის შეცვლა" + "შეტყობინებების გაგზავნა" + "ადმინისტრატორების რედაქტირება" + "ამ მოქმედების გაუქმებას ვერ შეძლებთ. თქვენ ნიშნავთ ამ მომხმარებელს იმავე ძალაუფლების დონეზე, რომელიც გაქვთ თქვენ." + "ადმინისტრატორის დამატება?" + "დაქვეითება" + "იმის გამო, რომ აქვეითებთ თქვენ თავს, ამ მოქმედებას ვერ გააუქმებთ. პრივილეგიების აღდგენა შეუძლებელია თუ თქვენ ბოლო პრივილეგირებული მომხმარებელი ხართ ამ ოთახში." + "გსურთ საკუთარი თავის დაქვეითება?" + "%1$s (მოლოდინი)" + "(მოლოდინში)" + "მოდერატორების რედაქტირება" + "ადმინისტრატორები" + "მოდერატორები" + "წევრები" + "თქვენ გაქვთ შეუნახავი ცვლილებები" + "შენახვა?" "თემის დამატება" "უკვე წევრია" "უკვე მოწვეულია" @@ -16,21 +43,44 @@ "ამ ოთახის დადუმება ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა." "ამ ოთახის დადუმების მოხსნა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა." "ხალხის მოწვევა" + "საუბრის დატოვება" "ოთახის დატოვება" "მორგებული" "ნაგულისხმევი" "შეტყობინებები" + "როლები და ნებართვები" "ოთახის სახელი" "უსაფრთხოება" "ოთახის გაზიარება" + "ოთახის ინფორმაცია" "თემა" "ოთახის განახლება…" + "დაბლოკვა" + "მოწვევის შემთხვევაში ამ ოთახში კვლავ გაწევრიანებას ვერ შეძლებენ." + "დარწმუნებული ხართ, რომ ამ წევრის დაბლოკვა გსურთ?" + "ამ ოთახში არაა დაბლოკილი მომხმარებლები." + "%1$s-ს დაბლოკვა" "%1$d ადამიანი" "%1$d ადამიანი" + "წევრის წაშლა და დაბლოკვა" + "ოთახიდან გაგდება" + "წევრის წაშლა და დაბლოკვა" + "მხოლოდ წევრის წაშლა" + "გსურთ წევრის გაგდება და მომავალში გაწევრიანების აკრძალვა?" + "განბლოკვა" + "მოწვევის შემთხვევაში განბლოკილი მომხმარებელი ისევ შეძლებს ოთახს შეუერთდეს." + "მომხმარებლის განბლოკვა" + "პროფილის ნახვა" + "დაბლოკილები" + "წევრები" "მომლოდინე" + "%1$s-ს გაგდება…" + "ადმინისტრატორი" + "მოდერატორი" "ოთახის წევრები" + "%1$s-ს განბლოკვა" "მორგებული პარამეტრის დაშვება" "ამის ჩართვა უგულებელყოფს თქვენს ნაგულისხმევ პარამეტრს" "ამ ჩატში ჩემი შეტყობინება:" @@ -45,4 +95,18 @@ "ყველა შეტყობინება" "მხოლოდ ხსენებები და საკვანძო სიტყვები" "ამ ოთახში, შემატყობინეთ:" + "ადმინისტრატორები" + "ჩემი როლის შეცვლა" + "დაქვეითება წევრამდე" + "დაქვეითება მოდერატორამდე" + "წევრების მოდერირება" + "შეტყობინებები და შინაარსი" + "მოდერატორები" + "ნებართვები" + "ნებართვების გადაყენება" + "ნებართვების გადაყენების შემთხვევაში მიმდინარე პარამეტრებს დაკარგავთ." + "გადავაყენოთ ცვლილებები?" + "როლები" + "ოთახის დეტალები" + "როლები და ნებართვები" 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 5933128251..4dd6f3fbb1 100644 --- a/features/roomdetails/impl/src/main/res/values-nb/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nb/translations.xml @@ -1,8 +1,34 @@ + "Du trenger en adresse til rommet for å gjøre det synlig i katalogen." + "Romadresse" "Det oppstod en feil under oppdatering av varslingsinnstillingen." + "Hjemmeserveren din støtter ikke dette alternativet i krypterte rom, og det kan hende at du ikke blir varslet i enkelte rom." + "Avstemninger" + "Kun for administratorer" + "Forby folk" + "Fjern meldinger" "Alle" + "Inviter folk og godta forespørsler om å bli med" + "Meldinger og innhold" + "Administratorer og moderatorer" + "Fjern folk og avslå forespørsler om å bli med" + "Endre romavatar" + "Romdetaljer" + "Endre romnavn" + "Endre temaet til rommet" + "Send meldinger" + "Rediger administratorer" + "Du vil ikke kunne angre denne handlingen. Du forfremmer brukeren til å ha samme rettighetsnivå som deg." + "Legg til administrator?" + "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." + "Degradere deg selv?" + "%1$s (Venter)" + "Administratorer har automatisk moderatorrettigheter" + "Rediger moderatorer" "Administratorer" + "Moderatorer" "Medlemmer" "Du har endringer som ikke er lagret." "Lagre endringer?" @@ -23,10 +49,16 @@ "Inviter folk" "Forlat samtalen" "Forlat rommet" + "Medier og filer" "Tilpasset" "Standard" "Varslinger" + "Festede meldinger" + "Profil" + "Forespørsler om å bli med" + "Roller og tillatelser" "Romnavn" + "Sikkerhet og personvern" "Sikkerhet" "Del rom" "Informasjon om rommet" @@ -68,12 +100,39 @@ "Det oppstod en feil ved innlasting av varslingsinnstillinger." "Gjenoppretting av standardmodus mislyktes, prøv igjen." "Innstilling av modus mislyktes, prøv igjen." + "Hjemmeserveren din støtter ikke dette alternativet i krypterte rom, og du vil ikke bli varslet i dette rommet." "Alle meldinger" "Bare omtaler og nøkkelord" "I dette rommet, varsle meg om" "Administratorer" "Endre rollen min" + "Nedgradere til medlem" + "Nedgradere til moderator" + "Meldinger og innhold" + "Moderatorer" + "Tillatelser" + "Tilbakestill tillatelser" + "Når du har tilbakestilt tillatelsene, mister du gjeldende innstillinger." + "Vil du tilbakestille tillatelsene?" + "Roller" + "Romdetaljer" + "Roller og tillatelser" + "Legg til romadresse" + "Alle kan be om å bli med i rommet, men en administrator eller moderator må godta forespørselen." + "Be om å bli med" + "Ja, aktiver kryptering" + "Vil du aktivere kryptering?" + "Når kryptering er aktivert, kan det ikke deaktiveres." "Kryptering" + "Aktiver ende-til-ende-kryptering" + "Alle kan finne og bli med" + "Folk kan bare bli med hvis de er invitert" + "Kun for inviterte" "Områder støttes ikke for øyeblikket" "Medlemmer av område" + "Romadresse" + "Synlig i offentlig romkatalog" + "Medlemmer bare siden de ble invitert" + "Romsynlighet" + "Sikkerhet og personvern" 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 10a8539ffa..301fd600d9 100644 --- a/features/roomdetails/impl/src/main/res/values-nl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-nl/translations.xml @@ -48,7 +48,7 @@ "Het dempen opheffen voor deze kamer is mislukt. Probeer het opnieuw." "Mensen uitnodigen" "Gesprek verlaten" - "Ruimte verlaten" + "Kamer verlaten" "Aangepast" "Standaard" "Meldingen" diff --git a/features/roomdetails/impl/src/main/res/values-sv/translations.xml b/features/roomdetails/impl/src/main/res/values-sv/translations.xml index e8c180f0cd..aace77772d 100644 --- a/features/roomdetails/impl/src/main/res/values-sv/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sv/translations.xml @@ -56,6 +56,7 @@ "Förval" "Aviseringar" "Fästa meddelanden" + "Profil" "Begäran om att gå med" "Roller och behörigheter" "Rumsnamn" @@ -119,6 +120,7 @@ "Roller" "Rumsdetaljer" "Roller och behörigheter" + "Lägg till rumsadress" "Vem som helst kan be om att gå med i rummet men en administratör eller moderator måste acceptera begäran." "Be om att gå med" "Ja, aktivera kryptering" @@ -136,16 +138,19 @@ Vi rekommenderar inte att aktivera kryptering för rum som vem som helst kan hit "Tillgång till rum" "Utrymmen stöds för närvarande inte" "Utrymmesmedlemmar" + "Du behöver en rumsadress för att göra den synlig i rumskatalogen." "Rumsadress" "Tillåt att detta rum hittas genom att söka i den offentliga rumskatalogen på %1$s" + "Synlig i katalogen för offentliga rum" "Vem som helst" "Vem kan läsa historik" "Endast medlemmar sedan de bjöds in" "Endast medlemmar sedan det här alternativet har valts" "Rumsadresser är sätt att hitta och komma åt rum. Detta säkerställer också att du enkelt kan dela ditt rum med andra. Du kan välja att publicera ditt rum i din hemservers offentliga rumskatalog." - "Rumsadresser är sätt att hitta och komma åt rum. Detta säkerställer också att du enkelt kan dela ditt rum med andra. -Adressen krävs också för att rummet ska synas i den allmänna rumskatalogen på %1$s." + "Rumspublicering" + "Rumsadresser är sätt att hitta och komma åt rum. Detta säkerställer också att du enkelt kan dela ditt rum med andra. +Adressen krävs också för att göra rummet synligt i%1$s katalog för offentliga rum." "Rumssynlighet" "Säkerhet och sekretess" 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 aa7dae4539..0ab09b2fa9 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 @@ -9,11 +9,11 @@ "管理黑名單" "移除訊息" "所有人" - "邀請夥伴" + "邀請夥伴並接受加入請求" "成員管理" "訊息與內容" "管理員和版主" - "踢出成員" + "移除夥伴並拒絕加入請求" "變更聊天室大頭照" "聊天室資訊" "變更聊天室名稱" @@ -56,6 +56,7 @@ "預設" "通知" "釘選訊息" + "設定檔" "請求加入" "身份與權限" "聊天室名稱" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt index d9cc77be76..3cf5c02e47 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/MatrixRoomFixture.kt @@ -7,34 +7,43 @@ package io.element.android.features.roomdetails.impl +import io.element.android.libraries.matrix.api.core.RoomAlias 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.room.RoomMember import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.join.JoinRule import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.matrix.test.A_ROOM_ALIAS 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.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.tests.testutils.lambda.lambdaError fun aMatrixRoom( + sessionId: SessionId = A_SESSION_ID, roomId: RoomId = A_ROOM_ID, displayName: String = A_ROOM_NAME, rawName: String? = displayName, topic: String? = A_ROOM_TOPIC, avatarUrl: String? = AN_AVATAR_URL, + canonicalAlias: RoomAlias? = A_ROOM_ALIAS, isEncrypted: Boolean = true, isPublic: Boolean = true, isDirect: Boolean = false, joinRule: JoinRule? = null, + activeMemberCount: Long = 1, + joinedMemberCount: Long = 1, + invitedMemberCount: Long = 0, notificationSettingsService: FakeNotificationSettingsService = FakeNotificationSettingsService(), - emitRoomInfo: Boolean = false, canInviteResult: (UserId) -> Result = { lambdaError() }, canBanResult: (UserId) -> Result = { lambdaError() }, + canKickResult: (UserId) -> Result = { lambdaError() }, canSendStateResult: (UserId, StateEventType) -> Result = { _, _ -> lambdaError() }, userDisplayNameResult: (UserId) -> Result = { lambdaError() }, userAvatarUrlResult: () -> Result = { lambdaError() }, @@ -44,17 +53,20 @@ fun aMatrixRoom( removeAvatarResult: () -> Result = { lambdaError() }, canUserJoinCallResult: (UserId) -> Result = { lambdaError() }, getUpdatedMemberResult: (UserId) -> Result = { lambdaError() }, + userRoleResult: () -> Result = { lambdaError() }, + kickUserResult: (UserId, String?) -> Result = { _, _ -> lambdaError() }, + banUserResult: (UserId, String?) -> Result = { _, _ -> lambdaError() }, + unBanUserResult: (UserId, String?) -> Result = { _, _ -> lambdaError() }, + updateCanonicalAliasResult: (RoomAlias?, List) -> Result = { _, _ -> lambdaError() }, + publishRoomAliasInRoomDirectoryResult: (RoomAlias) -> Result = { lambdaError() }, + removeRoomAliasFromRoomDirectoryResult: (RoomAlias) -> Result = { lambdaError() }, ) = FakeMatrixRoom( + sessionId = sessionId, roomId = roomId, - displayName = displayName, - topic = topic, - avatarUrl = avatarUrl, - isEncrypted = isEncrypted, - isPublic = isPublic, - isDirect = isDirect, notificationSettingsService = notificationSettingsService, canInviteResult = canInviteResult, canBanResult = canBanResult, + canKickResult = canKickResult, canSendStateResult = canSendStateResult, userDisplayNameResult = userDisplayNameResult, userAvatarUrlResult = userAvatarUrlResult, @@ -64,17 +76,25 @@ fun aMatrixRoom( removeAvatarResult = removeAvatarResult, canUserJoinCallResult = canUserJoinCallResult, getUpdatedMemberResult = getUpdatedMemberResult, -).apply { - if (emitRoomInfo) { - givenRoomInfo( - aRoomInfo( - name = displayName, - rawName = rawName, - topic = topic, - avatarUrl = avatarUrl, - isDirect = isDirect, - joinRule = joinRule, - ) - ) - } -} + userRoleResult = userRoleResult, + kickUserResult = kickUserResult, + banUserResult = banUserResult, + unBanUserResult = unBanUserResult, + updateCanonicalAliasResult = updateCanonicalAliasResult, + publishRoomAliasInRoomDirectoryResult = publishRoomAliasInRoomDirectoryResult, + removeRoomAliasFromRoomDirectoryResult = removeRoomAliasFromRoomDirectoryResult, + initialRoomInfo = aRoomInfo( + name = displayName, + rawName = rawName, + topic = topic, + avatarUrl = avatarUrl, + canonicalAlias = canonicalAlias, + isDirect = isDirect, + isPublic = isPublic, + isEncrypted = isEncrypted, + joinRule = joinRule, + joinedMembersCount = joinedMemberCount, + activeMembersCount = activeMemberCount, + invitedMembersCount = invitedMemberCount, + ) +) 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 1eec78ea9c..07d10ede63 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 @@ -8,10 +8,6 @@ package io.element.android.features.roomdetails.impl import androidx.lifecycle.Lifecycle -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 im.vector.app.features.analytics.plan.Interaction import io.element.android.features.leaveroom.api.LeaveRoomEvent @@ -39,6 +35,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_TOPIC import io.element.android.libraries.matrix.test.A_SESSION_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.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo @@ -52,7 +49,7 @@ 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.testCoroutineDispatchers -import io.element.android.tests.testutils.withFakeLifecycleOwner +import io.element.android.tests.testutils.testWithLifecycleOwner import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -83,6 +80,7 @@ class RoomDetailsPresenterTest { ) ), isPinnedMessagesFeatureEnabled: Boolean = true, + encryptionService: FakeEncryptionService = FakeEncryptionService(), ): RoomDetailsPresenter { val matrixClient = FakeMatrixClient(notificationSettingsService = notificationSettingsService) val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { @@ -93,6 +91,7 @@ class RoomDetailsPresenterTest { userProfilePresenterFactory = { Presenter { aUserProfileState() } }, + encryptionService = encryptionService, ) } } @@ -110,39 +109,31 @@ class RoomDetailsPresenterTest { ) } - private suspend fun RoomDetailsPresenter.test(validate: suspend TurbineTestContext.() -> Unit) { - moleculeFlow(RecompositionMode.Immediate) { - withFakeLifecycleOwner(fakeLifecycleOwner) { - present() - } - }.test(validate = validate) - } - @Test - fun `present - initial state is created from room if roomInfo is null`() = runTest { + fun `present - initial state is created from initial room info`() = runTest { val room = aMatrixRoom( canInviteResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { - skipItems(1) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { val initialState = awaitItem() assertThat(initialState.roomId).isEqualTo(room.roomId) - assertThat(initialState.roomName).isEqualTo(room.displayName) - assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl) - assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!)) - assertThat(initialState.memberCount).isEqualTo(room.joinedMemberCount) - assertThat(initialState.isEncrypted).isEqualTo(room.isEncrypted) + assertThat(initialState.roomName).isEqualTo(room.info().name) + assertThat(initialState.roomAvatarUrl).isEqualTo(room.info().avatarUrl) + assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!)) + assertThat(initialState.memberCount).isEqualTo(room.info().joinedMembersCount) assertThat(initialState.canShowPinnedMessages).isTrue() - assertThat(initialState.pinnedMessagesCount).isNull() + assertThat(initialState.pinnedMessagesCount).isEqualTo(0) assertThat(initialState.canShowSecurityAndPrivacy).isFalse() + + cancelAndIgnoreRemainingEvents() } } @Test - fun `present - initial state is updated with roomInfo if it exists`() = runTest { + fun `present - initial state is updated with a new roomInfo`() = runTest { val roomInfo = aRoomInfo( name = A_ROOM_NAME, topic = A_ROOM_TOPIC, @@ -157,7 +148,7 @@ class RoomDetailsPresenterTest { givenRoomInfo(roomInfo) } val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) val updatedState = awaitItem() assertThat(updatedState.roomName).isEqualTo(roomInfo.name) @@ -177,9 +168,9 @@ class RoomDetailsPresenterTest { canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { val initialState = awaitItem() - assertThat(initialState.roomName).isEqualTo(room.displayName) + assertThat(initialState.roomName).isEqualTo(room.info().name) cancelAndIgnoreRemainingEvents() } @@ -190,8 +181,6 @@ class RoomDetailsPresenterTest { val myRoomMember = aRoomMember(A_SESSION_ID) val otherRoomMember = aRoomMember(A_USER_ID_2) val room = aMatrixRoom( - isEncrypted = true, - isDirect = true, canInviteResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, canSendStateResult = { _, _ -> Result.success(true) }, @@ -205,9 +194,16 @@ class RoomDetailsPresenterTest { ).apply { val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) + + givenRoomInfo( + aRoomInfo( + isEncrypted = true, + isDirect = true, + ) + ) } val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { val initialState = awaitItem() assertThat(initialState.roomType).isEqualTo( RoomDetailsType.Dm( @@ -227,7 +223,7 @@ class RoomDetailsPresenterTest { canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room, dispatchers = testCoroutineDispatchers()) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Initially false assertThat(awaitItem().canInvite).isFalse() // Then the asynchronous check completes and it becomes true @@ -245,7 +241,7 @@ class RoomDetailsPresenterTest { canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { assertThat(awaitItem().canInvite).isFalse() cancelAndIgnoreRemainingEvents() @@ -260,7 +256,7 @@ class RoomDetailsPresenterTest { canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { assertThat(awaitItem().canInvite).isFalse() cancelAndIgnoreRemainingEvents() @@ -281,7 +277,7 @@ class RoomDetailsPresenterTest { canUserJoinCallResult = { Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes and it becomes true @@ -296,8 +292,6 @@ class RoomDetailsPresenterTest { val myRoomMember = aRoomMember(A_SESSION_ID) val otherRoomMember = aRoomMember(A_USER_ID_2) val room = aMatrixRoom( - isEncrypted = true, - isDirect = true, canSendStateResult = { _, stateEventType -> when (stateEventType) { StateEventType.ROOM_TOPIC, @@ -318,16 +312,23 @@ class RoomDetailsPresenterTest { ).apply { val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) + + givenRoomInfo( + aRoomInfo( + isEncrypted = true, + isDirect = true, + ) + ) } val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes, but editing is still disallowed because it's a DM val settledState = awaitItem() assertThat(settledState.canEdit).isFalse() // If there is a topic, it's visible - assertThat(settledState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!)) + assertThat(settledState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.info().topic!!)) cancelAndIgnoreRemainingEvents() } @@ -338,7 +339,6 @@ class RoomDetailsPresenterTest { val myRoomMember = aRoomMember(A_SESSION_ID) val otherRoomMember = aRoomMember(A_USER_ID_2) val room = aMatrixRoom( - isEncrypted = true, isDirect = true, topic = null, canSendStateResult = { _, stateEventType -> @@ -361,10 +361,18 @@ class RoomDetailsPresenterTest { ).apply { val roomMembers = persistentListOf(myRoomMember, otherRoomMember) givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) + + givenRoomInfo( + aRoomInfo( + isDirect = true, + activeMembersCount = 2, + topic = null, + ) + ) } val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) // There's no topic, so we hide the entire UI for DMs @@ -391,7 +399,7 @@ class RoomDetailsPresenterTest { canUserJoinCallResult = { Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Initially false assertThat(awaitItem().canEdit).isFalse() // Then the asynchronous check completes and it becomes true @@ -418,7 +426,7 @@ class RoomDetailsPresenterTest { canUserJoinCallResult = { Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Initially false, and no further events assertThat(awaitItem().canEdit).isFalse() @@ -444,7 +452,7 @@ class RoomDetailsPresenterTest { canUserJoinCallResult = { Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // The initial state is "hidden" and no further state changes happen assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.Hidden) @@ -472,7 +480,7 @@ class RoomDetailsPresenterTest { givenRoomInfo(aRoomInfo(topic = null)) } val presenter = createRoomDetailsPresenter(room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { // Ignore the initial state skipItems(1) @@ -496,7 +504,7 @@ class RoomDetailsPresenterTest { leaveRoomState = aLeaveRoomState(eventSink = leaveRoomEventRecorder), dispatchers = testCoroutineDispatchers() ) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { awaitItem().eventSink(RoomDetailsEvent.LeaveRoom) leaveRoomEventRecorder.assertSingle(LeaveRoomEvent.ShowConfirmation(room.roomId)) cancelAndIgnoreRemainingEvents() @@ -516,7 +524,7 @@ class RoomDetailsPresenterTest { room = room, notificationSettingsService = notificationSettingsService, ) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { notificationSettingsService.setRoomNotificationMode( room.roomId, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY @@ -545,7 +553,7 @@ class RoomDetailsPresenterTest { room = room, notificationSettingsService = notificationSettingsService ) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { awaitItem().eventSink(RoomDetailsEvent.MuteNotification) val updatedState = consumeItemsUntilPredicate(timeout = 250.milliseconds) { it.roomNotificationSettings?.mode == RoomNotificationMode.MUTE @@ -573,7 +581,7 @@ class RoomDetailsPresenterTest { room = room, notificationSettingsService = notificationSettingsService ) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { awaitItem().eventSink(RoomDetailsEvent.UnmuteNotification) val updatedState = consumeItemsUntilPredicate { it.roomNotificationSettings?.mode == RoomNotificationMode.ALL_MESSAGES @@ -597,7 +605,7 @@ class RoomDetailsPresenterTest { val analyticsService = FakeAnalyticsService() val presenter = createRoomDetailsPresenter(room = room, analyticsService = analyticsService) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { val initialState = awaitItem() initialState.eventSink(RoomDetailsEvent.SetFavorite(true)) setIsFavoriteResult.assertions().isCalledOnce().with(value(true)) @@ -623,7 +631,7 @@ class RoomDetailsPresenterTest { canSendStateResult = { _, _ -> Result.success(true) }, ) val presenter = createRoomDetailsPresenter(room = room) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { room.givenRoomInfo(aRoomInfo(isFavorite = true)) consumeItemsUntilPredicate { it.isFavorite }.last().let { state -> assertThat(state.isFavorite).isTrue() @@ -639,7 +647,6 @@ class RoomDetailsPresenterTest { @Test fun `present - show knock requests`() = runTest { val room = aMatrixRoom( - emitRoomInfo = true, canInviteResult = { Result.success(true) }, canUserJoinCallResult = { Result.success(true) }, canSendStateResult = { _, _ -> Result.success(true) }, @@ -652,19 +659,14 @@ class RoomDetailsPresenterTest { room = room, featureFlagService = featureFlagService, ) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) - with(awaitItem()) { - assertThat(canShowKnockRequests).isFalse() - } + assertThat(awaitItem().canShowKnockRequests).isFalse() featureFlagService.setFeatureEnabled(FeatureFlags.Knock, true) - with(awaitItem()) { - assertThat(canShowKnockRequests).isTrue() - } + assertThat(awaitItem().canShowKnockRequests).isTrue() room.givenRoomInfo(aRoomInfo(joinRule = JoinRule.Private)) - with(awaitItem()) { - assertThat(canShowKnockRequests).isFalse() - } + assertThat(awaitItem().canShowKnockRequests).isFalse() + cancelAndIgnoreRemainingEvents() } } @@ -677,7 +679,7 @@ class RoomDetailsPresenterTest { ) val featureFlagService = FakeFeatureFlagService() val presenter = createRoomDetailsPresenter(room = room, featureFlagService = featureFlagService) - presenter.test { + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { skipItems(1) with(awaitItem()) { assertThat(canShowSecurityAndPrivacy).isFalse() diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt index 8cc1af1638..da7f92aed5 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsViewTest.kt @@ -15,7 +15,10 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.roomdetails.impl.members.aRoomMember +import io.element.android.features.userprofile.shared.aUserProfileState +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomNotificationMode +import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureCalledOnceWithTwoParams @@ -294,6 +297,21 @@ class RoomDetailsViewTest { rule.clickOn(R.string.screen_room_details_requests_to_join_title) } } + + @Config(qualifiers = "h1024dp") + @Test + fun `click on profile invokes the expected callback`() { + ensureCalledOnceWithParam(A_USER_ID) { callback -> + rule.setRoomDetailView( + state = aRoomDetailsState( + eventSink = EventsRecorder(expectEvents = false), + roomMemberDetailsState = aUserProfileState(userId = A_USER_ID), + ), + onProfileClick = callback, + ) + rule.clickOn(R.string.screen_room_details_profile_row_title) + } + } } private fun AndroidComposeTestRule.setRoomDetailView( @@ -314,6 +332,7 @@ private fun AndroidComposeTestRule.setRoomD onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(), onKnockRequestsClick: () -> Unit = EnsureNeverCalled(), onSecurityAndPrivacyClick: () -> Unit = EnsureNeverCalled(), + onProfileClick: (UserId) -> Unit = EnsureNeverCalledWithParam(), ) { setContent { RoomDetailsView( @@ -332,6 +351,7 @@ private fun AndroidComposeTestRule.setRoomD onPinnedMessagesClick = onPinnedMessagesClick, onKnockRequestsClick = onKnockRequestsClick, onSecurityAndPrivacyClick = onSecurityAndPrivacyClick, + onProfileClick = onProfileClick, ) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt index 26c9f99105..763f94c310 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenterTest.kt @@ -93,7 +93,6 @@ class RoomDetailsEditPresenterTest { avatarUrl = AN_AVATAR_URL, displayName = A_ROOM_NAME, rawName = A_ROOM_RAW_NAME, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) val deleteCallback = lambdaRecorder {} @@ -106,7 +105,7 @@ class RoomDetailsEditPresenterTest { assertThat(initialState.roomId).isEqualTo(room.roomId) assertThat(initialState.roomRawName).isEqualTo(A_ROOM_RAW_NAME) assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) - assertThat(initialState.roomTopic).isEqualTo(room.topic.orEmpty()) + assertThat(initialState.roomTopic).isEqualTo(room.info().topic.orEmpty()) assertThat(initialState.avatarActions).containsExactly( AvatarAction.ChoosePhoto, AvatarAction.TakePhoto, @@ -220,7 +219,6 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) val deleteCallback = lambdaRecorder {} @@ -266,7 +264,6 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(anotherAvatarUri) @@ -291,7 +288,6 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(anotherAvatarUri) @@ -319,8 +315,7 @@ class RoomDetailsEditPresenterTest { stateWithNewAvatar.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) val stateWithNewAvatar2 = awaitItem() assertThat(stateWithNewAvatar2.roomAvatarUrl).isEqualTo(roomAvatarUri) - deleteCallback.assertions().isCalledExactly(4).withSequence( - listOf(value(null)), + deleteCallback.assertions().isCalledExactly(3).withSequence( listOf(value(null)), listOf(value(roomAvatarUri)), listOf(value(anotherAvatarUri)), @@ -334,7 +329,6 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(roomAvatarUri) @@ -385,7 +379,6 @@ class RoomDetailsEditPresenterTest { topic = null, displayName = "fallback", avatarUrl = null, - emitRoomInfo = true, canSendStateResult = { _, _ -> Result.success(true) } ) fakePickerProvider.givenResult(roomAvatarUri) @@ -439,7 +432,6 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, setNameResult = setNameResult, setTopicResult = setTopicResult, removeAvatarResult = removeAvatarResult, @@ -553,7 +545,7 @@ class RoomDetailsEditPresenterTest { updateAvatarResult.assertions().isCalledOnce().with(value(MimeTypes.Jpeg), value(fakeFileContents)) deleteCallback.assertions().isCalledExactly(2).withSequence( listOf(value(null)), - listOf(value(null)), + listOf(value(roomAvatarUri)), ) } } @@ -588,11 +580,10 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, setNameResult = { Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name")) + saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name"), deleteCallbackNumberOfInvocation = 1) } @Test @@ -601,11 +592,10 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, setTopicResult = { Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic")) + saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic"), deleteCallbackNumberOfInvocation = 1) } @Test @@ -614,11 +604,10 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, removeAvatarResult = { Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 3) + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove), deleteCallbackNumberOfInvocation = 2) } @Test @@ -628,11 +617,10 @@ class RoomDetailsEditPresenterTest { topic = "My topic", displayName = "Name", avatarUrl = AN_AVATAR_URL, - emitRoomInfo = true, updateAvatarResult = { _, _ -> Result.failure(Throwable("!")) }, canSendStateResult = { _, _ -> Result.success(true) } ) - saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 3) + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto), deleteCallbackNumberOfInvocation = 2) } @Test @@ -704,6 +692,6 @@ class RoomDetailsEditPresenterTest { } private suspend fun ReceiveTurbine.awaitFirstItem(): T { - skipItems(2) + skipItems(1) return awaitItem() } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenterTest.kt index 0200b8116d..91a6ba6683 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListPresenterTest.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.tests.testutils.EventsRecorder @@ -59,7 +60,8 @@ class RoomMemberListPresenterTest { skipItems(1) val loadedMembersState = awaitItem() assertThat(loadedMembersState.roomMembers.isLoading()).isFalse() - assertThat(loadedMembersState.roomMembers.dataOrNull()?.invited).isEqualTo(listOf(aVictor(), aWalter())) + assertThat(loadedMembersState.roomMembers.dataOrNull()?.invited) + .isEqualTo(listOf(RoomMemberWithIdentityState(aVictor(), null), RoomMemberWithIdentityState(aWalter(), null))) assertThat(loadedMembersState.roomMembers.dataOrNull()?.joined).isNotEmpty() } } @@ -129,7 +131,7 @@ class RoomMemberListPresenterTest { assertThat(searchQueryUpdatedState.searchQuery).isEqualTo("Alice") val searchSearchResultDelivered = awaitItem() assertThat(searchSearchResultDelivered.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) - assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.dataOrNull()!!.joined.first().displayName) + assertThat((searchSearchResultDelivered.searchResults as SearchBarResultState.Results).results.dataOrNull()!!.joined.first().roomMember.displayName) .isEqualTo("Alice") } } @@ -259,11 +261,13 @@ private fun TestScope.createPresenter( ), roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers), roomMembersModerationStateLambda: () -> RoomMembersModerationState = { aRoomMembersModerationState() }, + encryptedService: FakeEncryptionService = FakeEncryptionService(), navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {} ) = RoomMemberListPresenter( room = matrixRoom, roomMemberListDataSource = roomMemberListDataSource, coroutineDispatchers = coroutineDispatchers, roomMembersModerationPresenter = { roomMembersModerationStateLambda() }, - navigator = navigator + encryptionService = encryptedService, + navigator = navigator, ) diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt index 95e3b47ce3..1506f50f42 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenterTest.kt @@ -13,14 +13,25 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.roomdetails.impl.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember +import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfilePresenterFactory +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState +import io.element.android.libraries.matrix.api.encryption.identity.IdentityStateChange import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState 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.encryption.FakeEncryptionService +import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.consumeItemsUntilPredicate +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.test import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -45,9 +56,7 @@ class RoomMemberDetailsPresenterTest { val presenter = createRoomMemberDetailsPresenter( room = room, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.test { val initialState = awaitItem() assertThat(initialState.userName).isEqualTo("Alice") assertThat(initialState.avatarUrl).isEqualTo("Profile avatar url") @@ -156,6 +165,180 @@ class RoomMemberDetailsPresenterTest { } } + @Test + fun `present - when user's identity is verified, the value in the state is VERIFIED`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(IdentityState.Verified) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFIED } + } + } + + @Test + fun `present - when user's identity is unknown, the value in the state is UNKNOWN`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(null) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + ensureAllEventsConsumed() + } + } + + @Test + fun `present - when user's identity is pinned, the value in the state is UNVERIFIED`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(IdentityState.Pinned) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.UNVERIFIED } + } + } + + @Test + fun `present - when user's identity is pin violation, the value in the state is UNVERIFIED`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(IdentityState.PinViolation) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.UNVERIFIED } + } + } + + @Test + fun `present - when user's identity has a verification violation, the value in the state is VERIFICATION_VIOLATION`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(IdentityState.VerificationViolation) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION } + } + } + + @Test + fun `present - user identity updates in real time if the room is encrypted`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(null) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + + room.emitSyncUpdate() + + room.emitIdentityStateChanges(listOf(IdentityStateChange(A_USER_ID, IdentityState.Pinned))) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.UNVERIFIED } + + room.emitIdentityStateChanges(listOf(IdentityStateChange(A_USER_ID, IdentityState.Verified))) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFIED } + + room.emitIdentityStateChanges(listOf(IdentityStateChange(A_USER_ID, IdentityState.VerificationViolation))) + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION } + } + } + + @Test + fun `present - user identity can't update in real time if the room is not encrypted`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = false), + ) + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(null) }, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + assertThat(awaitItem().verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + + room.emitSyncUpdate() + room.emitIdentityStateChanges(listOf(IdentityStateChange(A_USER_ID, IdentityState.Pinned))) + + // No new events emitted + ensureAllEventsConsumed() + } + } + + @Test + fun `present - handles WithdrawVerification action`() = runTest { + val room = FakeMatrixRoom( + getUpdatedMemberResult = { Result.success(aRoomMember(A_USER_ID)) }, + userDisplayNameResult = { Result.success("A custom name") }, + userAvatarUrlResult = { Result.success("A custom avatar") }, + initialRoomInfo = aRoomInfo(isEncrypted = true), + ) + val withdrawVerificationResult = lambdaRecorder> { Result.success(Unit) } + val encryptionService = FakeEncryptionService( + getUserIdentityResult = { Result.success(IdentityState.VerificationViolation) }, + withdrawVerificationResult = withdrawVerificationResult, + ) + val presenter = createRoomMemberDetailsPresenter(room = room, encryptionService = encryptionService) + presenter.test { + // Initial state, then the verification state is updated + val initialState = awaitItem() + assertThat(initialState.verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) + + consumeItemsUntilPredicate { it.verificationState == UserProfileVerificationState.VERIFICATION_VIOLATION } + + initialState.eventSink(UserProfileEvents.WithdrawVerification) + withdrawVerificationResult.assertions().isCalledOnce() + } + } + private fun createRoomMemberDetailsPresenter( room: MatrixRoom, userProfilePresenterFactory: UserProfilePresenterFactory = UserProfilePresenterFactory { @@ -166,11 +349,13 @@ class RoomMemberDetailsPresenterTest { ) } }, + encryptionService: FakeEncryptionService = FakeEncryptionService(getUserIdentityResult = { Result.success(null) }), ): RoomMemberDetailsPresenter { return RoomMemberDetailsPresenter( roomMemberId = UserId("@alice:server.org"), room = room, - userProfilePresenterFactory = userProfilePresenterFactory + userProfilePresenterFactory = userProfilePresenterFactory, + encryptionService = encryptionService, ) } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenterTest.kt index d569a93baa..d86fc49925 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/members/moderation/RoomMembersModerationPresenterTest.kt @@ -12,6 +12,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.RoomModeration +import io.element.android.features.roomdetails.impl.aMatrixRoom import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.aVictor import io.element.android.libraries.architecture.AsyncAction @@ -34,8 +35,7 @@ import org.junit.Test class RoomMembersModerationPresenterTest { @Test fun `canDisplayModerationActions - when room is DM is false`() = runTest { - val room = FakeMatrixRoom( - isDirect = true, + val room = aMatrixRoom( isPublic = true, activeMemberCount = 2, canKickResult = { Result.success(true) }, @@ -52,8 +52,7 @@ class RoomMembersModerationPresenterTest { @Test fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest { - val room = FakeMatrixRoom( - isDirect = false, + val room = aMatrixRoom( activeMemberCount = 10, canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, @@ -68,8 +67,7 @@ class RoomMembersModerationPresenterTest { @Test fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest { - val room = FakeMatrixRoom( - isDirect = false, + val room = aMatrixRoom( activeMemberCount = 10, canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, @@ -84,7 +82,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - SelectRoomMember when the current user has permissions displays member actions`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, @@ -110,7 +108,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - SelectRoomMember displays only view profile if selected member has same power level as the current user`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( sessionId = A_USER_ID, canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, @@ -136,7 +134,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - SelectRoomMember displays an unban confirmation dialog when the member is banned`() = runTest { val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN) - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, @@ -157,7 +155,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - Kick removes the user`() = runTest { val analyticsService = FakeAnalyticsService() - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, @@ -186,7 +184,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - BanUser requires confirmation and then bans the user`() = runTest { val analyticsService = FakeAnalyticsService() - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, @@ -222,7 +220,7 @@ class RoomMembersModerationPresenterTest { fun `present - UnbanUser requires confirmation and then unbans the user`() = runTest { val analyticsService = FakeAnalyticsService() val selectedMember = aRoomMember(A_USER_ID_2, membership = RoomMembershipState.BAN) - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.ADMIN) }, @@ -254,7 +252,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - Reset removes the selected user and actions`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, userRoleResult = { Result.success(RoomMember.Role.USER) }, @@ -276,7 +274,7 @@ class RoomMembersModerationPresenterTest { @Test fun `present - Reset resets any async actions`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( canKickResult = { Result.success(true) }, canBanResult = { Result.success(true) }, kickUserResult = { _, _ -> Result.failure(Throwable("Eek")) }, @@ -324,7 +322,7 @@ class RoomMembersModerationPresenterTest { } private fun TestScope.createRoomMembersModerationPresenter( - matrixRoom: FakeMatrixRoom = FakeMatrixRoom(), + matrixRoom: FakeMatrixRoom = aMatrixRoom(), dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), ): RoomMembersModerationPresenter { diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenterTest.kt index 1d94c72011..1c419c9d1f 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/SecurityAndPrivacyPresenterTest.kt @@ -56,17 +56,15 @@ class SecurityAndPrivacyPresenterTest { fun `present - room info change updates saved and edited settings`() = runTest { val room = FakeMatrixRoom( canSendStateResult = { _, _ -> Result.success(true) }, + initialRoomInfo = aRoomInfo( + joinRule = JoinRule.Public, + historyVisibility = RoomHistoryVisibility.WorldReadable, + canonicalAlias = A_ROOM_ALIAS, + ) ) val presenter = createSecurityAndPrivacyPresenter(room = room) presenter.test { - skipItems(2) - room.givenRoomInfo( - aRoomInfo( - joinRule = JoinRule.Public, - historyVisibility = RoomHistoryVisibility.WorldReadable, - canonicalAlias = A_ROOM_ALIAS, - ) - ) + skipItems(1) with(awaitItem()) { assertThat(editedSettings).isEqualTo(savedSettings) assertThat(editedSettings.roomAccess).isEqualTo(SecurityAndPrivacyRoomAccess.Anyone) @@ -151,6 +149,7 @@ class SecurityAndPrivacyPresenterTest { assertThat(canBeSaved).isTrue() eventSink(SecurityAndPrivacyEvents.ToggleEncryptionState) } + skipItems(1) with(awaitItem()) { assertThat(editedSettings.isEncrypted).isFalse() assertThat(canBeSaved).isFalse() @@ -162,7 +161,8 @@ class SecurityAndPrivacyPresenterTest { fun `present - room visibility loading and change`() = runTest { val room = FakeMatrixRoom( canSendStateResult = { _, _ -> Result.success(true) }, - roomVisibilityResult = { Result.success(RoomVisibility.Private) } + roomVisibilityResult = { Result.success(RoomVisibility.Private) }, + initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared) ) val presenter = createSecurityAndPrivacyPresenter(room = room) presenter.test { @@ -212,7 +212,8 @@ class SecurityAndPrivacyPresenterTest { updateJoinRuleResult = updateJoinRuleLambda, updateRoomVisibilityResult = updateRoomVisibilityLambda, updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda, - roomVisibilityResult = { Result.success(RoomVisibility.Private) } + roomVisibilityResult = { Result.success(RoomVisibility.Private) }, + initialRoomInfo = aRoomInfo(joinRule = JoinRule.Invite, historyVisibility = RoomHistoryVisibility.Shared) ) val presenter = createSecurityAndPrivacyPresenter(room = room) presenter.test { @@ -245,6 +246,7 @@ class SecurityAndPrivacyPresenterTest { aRoomInfo( joinRule = JoinRule.Public, historyVisibility = RoomHistoryVisibility.WorldReadable, + isEncrypted = true, ) ) // Saved settings are updated 3 times to match the edited settings @@ -275,7 +277,8 @@ class SecurityAndPrivacyPresenterTest { updateJoinRuleResult = updateJoinRuleLambda, updateRoomVisibilityResult = updateRoomVisibilityLambda, updateRoomHistoryVisibilityResult = updateRoomHistoryVisibilityLambda, - roomVisibilityResult = { Result.success(RoomVisibility.Private) } + roomVisibilityResult = { Result.success(RoomVisibility.Private) }, + initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private) ) val presenter = createSecurityAndPrivacyPresenter(room = room) presenter.test { @@ -311,7 +314,7 @@ class SecurityAndPrivacyPresenterTest { ) ) // Saved settings are updated 2 times to match the edited settings - skipItems(2) + skipItems(3) with(awaitItem()) { assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java) assertThat(savedSettings.isVisibleInRoomDirectory).isNotEqualTo(editedSettings.isVisibleInRoomDirectory) @@ -328,7 +331,8 @@ class SecurityAndPrivacyPresenterTest { serverName: String = "matrix.org", room: MatrixRoom = FakeMatrixRoom( canSendStateResult = { _, _ -> Result.success(true) }, - roomVisibilityResult = { Result.success(RoomVisibility.Private) } + roomVisibilityResult = { Result.success(RoomVisibility.Private) }, + initialRoomInfo = aRoomInfo(historyVisibility = RoomHistoryVisibility.Shared, joinRule = JoinRule.Private) ), navigator: SecurityAndPrivacyNavigator = FakeSecurityAndPrivacyNavigator(), ): SecurityAndPrivacyPresenter { diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenterTest.kt index 8c30f3d4fa..845e2c0272 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/securityandprivacy/editroomaddress/EditRoomAddressPresenterTest.kt @@ -8,6 +8,7 @@ package io.element.android.features.roomdetails.impl.securityandprivacy.editroomaddress import com.google.common.truth.Truth.assertThat +import io.element.android.features.roomdetails.impl.aMatrixRoom import io.element.android.features.roomdetails.impl.securityandprivacy.FakeSecurityAndPrivacyNavigator import io.element.android.features.roomdetails.impl.securityandprivacy.SecurityAndPrivacyNavigator import io.element.android.libraries.architecture.AsyncAction @@ -31,7 +32,9 @@ import java.util.Optional class EditRoomAddressPresenterTest { @Test fun `present - initial state no address`() = runTest { - val presenter = createEditRoomAddressPresenter() + val presenter = createEditRoomAddressPresenter( + room = aMatrixRoom(displayName = "") + ) presenter.test { with(awaitItem()) { assertThat(homeserverName).isEqualTo("matrix.org") @@ -45,7 +48,7 @@ class EditRoomAddressPresenterTest { @Test fun `present - initial state address matching own homeserver`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( canonicalAlias = RoomAlias("#canonical:matrix.org"), ) val presenter = createEditRoomAddressPresenter(room = room) @@ -62,7 +65,8 @@ class EditRoomAddressPresenterTest { @Test fun `present - initial state address not matching own homeserver`() = runTest { - val room = FakeMatrixRoom( + val room = aMatrixRoom( + displayName = "", canonicalAlias = RoomAlias("#canonical:notmatrix.org"), ) val presenter = createEditRoomAddressPresenter(room = room) @@ -190,7 +194,7 @@ class EditRoomAddressPresenterTest { val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda) val canonicalAlias = RoomAlias("#canonical:matrix.org") - val room = FakeMatrixRoom( + val room = aMatrixRoom( canonicalAlias = canonicalAlias, updateCanonicalAliasResult = updateCanonicalAliasResult, publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult, @@ -240,7 +244,7 @@ class EditRoomAddressPresenterTest { val navigator = FakeSecurityAndPrivacyNavigator(closeEditRoomAddressLambda = closeEditAddressLambda) val canonicalAlias = RoomAlias("#canonical:notmatrix.org") - val room = FakeMatrixRoom( + val room = aMatrixRoom( canonicalAlias = canonicalAlias, updateCanonicalAliasResult = updateCanonicalAliasResult, publishRoomAliasInRoomDirectoryResult = publishAliasInRoomDirectoryResult, @@ -314,6 +318,7 @@ class EditRoomAddressPresenterTest { with(awaitItem()) { eventSink(EditRoomAddressEvents.Save) } + assertThat(awaitItem().saveAction).isInstanceOf(AsyncAction.Loading::class.java) with(awaitItem()) { assertThat(saveAction).isInstanceOf(AsyncAction.Failure::class.java) eventSink(EditRoomAddressEvents.DismissError) diff --git a/features/roomdirectory/impl/src/main/res/values-es/translations.xml b/features/roomdirectory/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..341799cc6c --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,5 @@ + + + "Carga fallida" + "Directorio de salas" + diff --git a/features/roomdirectory/impl/src/main/res/values-eu/translations.xml b/features/roomdirectory/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..63c2cda14a --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,5 @@ + + + "Ezin izan da kargatu" + "Gelen direktorioa" + diff --git a/features/roomdirectory/impl/src/main/res/values-ka/translations.xml b/features/roomdirectory/impl/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..86290f0728 --- /dev/null +++ b/features/roomdirectory/impl/src/main/res/values-ka/translations.xml @@ -0,0 +1,5 @@ + + + "ჩატვირთვა წარუმატებელია" + "ოთახის კატალოგი" + diff --git a/features/roomlist/impl/src/main/res/values-be/translations.xml b/features/roomlist/impl/src/main/res/values-be/translations.xml index d7d20060c8..2f2f943249 100644 --- a/features/roomlist/impl/src/main/res/values-be/translations.xml +++ b/features/roomlist/impl/src/main/res/values-be/translations.xml @@ -37,7 +37,6 @@ "Усе чаты" "Пазначыць як прачытанае" "Пазначыць як непрачытанае" - "Праглядзець усе пакоі" "Здаецца, вы карыстаецеся новай прыладай. Праверце з дапамогай іншай прылады, каб атрымаць доступ да зашыфраваных паведамленняў." "Пацвердзіце, што гэта вы" diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml index 795edcfe0c..927f9a40b9 100644 --- a/features/roomlist/impl/src/main/res/values-cs/translations.xml +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -40,7 +40,6 @@ Nemáte žádné nepřečtené zprávy!" "Všechny chaty" "Označit jako přečtené" "Označit jako nepřečtené" - "Procházet všechny místnosti" "Zdá se, že používáte nové zařízení. Ověřte přihlášení, abyste měli přístup k zašifrovaným zprávám." "Ověřte, že jste to vy" diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index 8fa6b7c756..80df9fb500 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -40,7 +40,6 @@ Du hast keine ungelesenen Nachrichten!" "Chats" "Als gelesen markieren" "Als ungelesen markieren" - "Alle Räume durchsuchen" "Es sieht aus, als würdest du ein neues Gerät verwenden. Verifiziere es mit einem anderen Gerät, damit du auf deine verschlüsselten Nachrichten zugreifen kannst." "Bestätige deine Identität" diff --git a/features/roomlist/impl/src/main/res/values-el/translations.xml b/features/roomlist/impl/src/main/res/values-el/translations.xml index b92c7daa90..cd91ad7efa 100644 --- a/features/roomlist/impl/src/main/res/values-el/translations.xml +++ b/features/roomlist/impl/src/main/res/values-el/translations.xml @@ -40,7 +40,6 @@ "Συνομιλίες" "Επισήμανση ως αναγνωσμένου" "Επισήμανση ως μη αναγνωσμένου" - "Περιήγηση σε όλα τα δωμάτια" "Φαίνεται ότι χρησιμοποιείς μια νέα συσκευή. Επαλήθευσε με άλλη συσκευή για πρόσβαση στα κρυπτογραφημένα σου μηνύματα." "Επαλήθευσε ότι είσαι εσύ" diff --git a/features/roomlist/impl/src/main/res/values-es/translations.xml b/features/roomlist/impl/src/main/res/values-es/translations.xml index 396f125994..fced9f1141 100644 --- a/features/roomlist/impl/src/main/res/values-es/translations.xml +++ b/features/roomlist/impl/src/main/res/values-es/translations.xml @@ -1,14 +1,21 @@ + "Recupera tu identidad criptográfica y tu historial de mensajes con una clave de recuperación si has perdido todos tus dispositivos actuales." "Configurar la recuperación" - "La copia de seguridad del chat no está sincronizada en este momento. Debes confirmar tu clave de recuperación para mantener el acceso a la copia de seguridad del chat." - "Confirma tu clave de recuperación" + "Configura la recuperación para proteger tu cuenta" + "Confirma tu clave de recuperación para mantener el acceso a tu almacén de claves y al historial de mensajes." + "Introduce tu clave de recuperación" + "¿Olvidaste tu clave de recuperación?" + "Tu almacén de claves no está sincronizado" + "Para asegurarte de que nunca te pierdas una llamada importante, modifica tus ajustes para permitir notificaciones a pantalla completa cuando el teléfono esté bloqueado." + "Mejora tu experiencia de llamada" "¿Estás seguro de que quieres rechazar la invitación a unirte a %1$s?" "Rechazar la invitación" - "¿Estás seguro de que quieres rechazar este chat privado con%1$s?" + "¿Estás seguro de que quieres rechazar este chat privado con %1$s?" "Rechazar el chat" "Sin invitaciones" "%1$s (%2$s) te invitó" + "Solicitud de unión enviada" "Este proceso solo se hace una vez, gracias por esperar." "Configura tu cuenta" "Crear una nueva conversación o sala" @@ -19,6 +26,7 @@ Por ahora, puedes deseleccionar los filtros para ver tus otros chats" "Aún no tienes chats favoritos" "Invitaciones" + "No tienes ninguna invitación pendiente." "Prioridad baja" "Puedes deseleccionar filtros para ver tus otros chats." "No tienes chats para esta selección" @@ -29,7 +37,7 @@ Por ahora, puedes deseleccionar los filtros para ver tus otros chats" "No leídos" "¡Felicidades! ¡No tienes ningún mensaje sin leer!" - "Todos los chats" + "Chats" "Marcar como leído" "Marcar como no leído" "Parece que estás usando un nuevo dispositivo. Verifica que eres tú para acceder a tus mensajes cifrados." diff --git a/features/roomlist/impl/src/main/res/values-et/translations.xml b/features/roomlist/impl/src/main/res/values-et/translations.xml index f642e34457..a7e3f822bf 100644 --- a/features/roomlist/impl/src/main/res/values-et/translations.xml +++ b/features/roomlist/impl/src/main/res/values-et/translations.xml @@ -40,7 +40,6 @@ Sul pole ühtegi lugemata sõnumit!" "Vestlused" "Märgi loetuks" "Märgi mitteloetuks" - "Sirvi kõiki jututube" "Tundub, et kasutad uut seadet. Oma krüptitud sõnumite lugemiseks verifitseeri ta mõne muu oma seadmega." "Verifitseeri, et see oled sina" diff --git a/features/roomlist/impl/src/main/res/values-eu/translations.xml b/features/roomlist/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..918c926b02 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,40 @@ + + + "Konfiguratu berreskurapena" + "Berreskuratze-gakoa ahaztu al duzu?" + "Dei garrantzitsurik galduko ez duzula ziurtatzeko, aldatu ezarpenak telefonoa blokeatuta dagoenean pantaila osoko jakinarazpenak baimentzeko." + "Hobetu deien esperientzia" + "Ziur %1$s(e)ra batzeko gonbidapena baztertu nahi duzula?" + "Baztertu gonbidapena" + "Ziur %1$s(r)en txat pribatua baztertu nahi duzula?" + "Baztertu txata" + "Ez dago gonbidapenik" + "%1$s(e)k (%2$s) gonbidatu zaitu" + "Sartzeko eskaera bidali da" + "Behin egin beharreko prozesua da; eskerrik asko itxaroteagatik." + "Zure kontua konfiguratzen." + "Sortu elkarrizketa edo gela berria" + "Hasi norbaiti mezuak bidaltzen." + "Oraindik ez dago txatik." + "Gogokoak" + "Txatak gogokoetara gehi dezakezu txaten ezarpenetan. +Oraingoz, iragazkiak desautatu ditzakezu zure gainerako txatak ikusteko" + "Oraindik ez duzu gogoko txatik" + "Gonbidapenak" + "Ez duzu gonbidapenik zain." + "Lehentasun baxua" + "Iragazkiak desautatu ditzakezu gainerako txatak ikusteko" + "Ez duzu hautaketa betetzen duen txatik" + "Jendea" + "Oraindik ez duzu Mezu Pribaturik" + "Gelak" + "Oraindik ez zaude inolako gelatan" + "Irakurri gabeak" + "Bejondeizula! +Ez duzu irakurri gabeko mezurik!" + "Txatak" + "Markatu irakurritzat" + "Markatu irakurri gabetzat" + "Gailu berri bat erabiltzen ari zarela dirudi. Egiaztatu beste gailu batekin enkriptatutako mezuak atzitzeko." + "Egiaztatu zu zarela" + diff --git a/features/roomlist/impl/src/main/res/values-fa/translations.xml b/features/roomlist/impl/src/main/res/values-fa/translations.xml index 2bccfdb38a..6ef6977b8a 100644 --- a/features/roomlist/impl/src/main/res/values-fa/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fa/translations.xml @@ -32,6 +32,5 @@ "گپ‌ها" "علامت‌گذاری به عنوان خوانده شده" "نشان به ناخوانده" - "مرور همهٔ اتاق‌ها" "تأیید کنید که خودتانید" diff --git a/features/roomlist/impl/src/main/res/values-fi/translations.xml b/features/roomlist/impl/src/main/res/values-fi/translations.xml index 9c79e556ac..27c4972452 100644 --- a/features/roomlist/impl/src/main/res/values-fi/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fi/translations.xml @@ -40,7 +40,6 @@ Sinulla ei ole lukemattomia viestejä!" "Keskustelut" "Merkitse luetuksi" "Merkitse lukemattomaksi" - "Selaa kaikkia huoneita" "Vaikuttaisi siltä, että käytät uutta laitetta. Vahvista toisella laitteella nähdäksesi salatut viestit." "Vahvista, että se olet sinä" diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml index 102edf2ee4..0c6805086a 100644 --- a/features/roomlist/impl/src/main/res/values-fr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -40,7 +40,6 @@ Vous n’avez plus de messages non-lus !" "Conversations" "Marquer comme lu" "Marquer comme non lu" - "Parcourir tous les salons" "Il semblerait que vous utilisiez un nouvel appareil. Vérifiez la session avec un autre de vos appareils pour accéder à vos messages chiffrés." "Vérifier que c’est bien vous" diff --git a/features/roomlist/impl/src/main/res/values-hu/translations.xml b/features/roomlist/impl/src/main/res/values-hu/translations.xml index aae22494a0..145919ffb7 100644 --- a/features/roomlist/impl/src/main/res/values-hu/translations.xml +++ b/features/roomlist/impl/src/main/res/values-hu/translations.xml @@ -40,7 +40,6 @@ Nincs olvasatlan üzenete!" "Összes csevegés" "Megjelölés olvasottként" "Megjelölés olvasatlanként" - "Összes szoba böngészése" "Úgy tűnik, hogy új eszközt használ. Ellenőrizze egy másik eszközzel, hogy a továbbiakban elérje a titkosított üzeneteket." "Ellenőrizze, hogy Ön az" diff --git a/features/roomlist/impl/src/main/res/values-in/translations.xml b/features/roomlist/impl/src/main/res/values-in/translations.xml index 936bb1e399..310e2a256c 100644 --- a/features/roomlist/impl/src/main/res/values-in/translations.xml +++ b/features/roomlist/impl/src/main/res/values-in/translations.xml @@ -40,7 +40,6 @@ Anda tidak memiliki pesan yang belum dibaca!" "Semua Obrolan" "Tandai sebagai dibaca" "Tandai sebagai belum dibaca" - "Cari semua ruangan" "Sepertinya Anda menggunakan perangkat baru. Verifikasi dengan perangkat lain untuk mengakses pesan terenkripsi Anda selanjutnya." "Verifikasi bahwa ini Anda" diff --git a/features/roomlist/impl/src/main/res/values-it/translations.xml b/features/roomlist/impl/src/main/res/values-it/translations.xml index 95b926f169..464267ab27 100644 --- a/features/roomlist/impl/src/main/res/values-it/translations.xml +++ b/features/roomlist/impl/src/main/res/values-it/translations.xml @@ -40,7 +40,6 @@ Non hai messaggi non letti!" "Tutte le conversazioni" "Segna come letto" "Segna come non letto" - "Sfoglia tutte le stanze" "Sembra che tu stia usando un nuovo dispositivo. Verificati con un altro dispositivo per accedere ai tuoi messaggi cifrati." "Verifica che sei tu" diff --git a/features/roomlist/impl/src/main/res/values-ka/translations.xml b/features/roomlist/impl/src/main/res/values-ka/translations.xml index 4fad6be272..bcedaeac60 100644 --- a/features/roomlist/impl/src/main/res/values-ka/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ka/translations.xml @@ -1,8 +1,8 @@ "აღდგენის დაყენება" - "თქვენი ჩეთების სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული. თქვენ უნდა შეიყვანოთ თქვენი აღდგენის გასაღები, რათა შეინარჩუნოთ წვდომა ჩეთების სარეზერვო ასლზე." - "შეიყვანეთ აღდგენის გასაღები" + "დაადასტურეთ თქვენი აღდგენის გასაღები რათა გქონდეთ წვდომა გასაღებების დამგროვებელთან და შეტყობინებების ისტორიასთან." + "თქვენი გასაღების დამგროვებელი არაა სინქრონიზებული" "დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ მოწვევაზე %1$s-ში?" "მოწვევაზე უარის თქმა" "დარწმუნებული ხართ, რომ გსურთ, უარი თქვათ ჩატზე %1$s-თან?" @@ -14,9 +14,24 @@ "ახალი საუბრისა ან ოთახის შექმნა" "დაიწყეთ ვინმესთვის შეტყობინების გაგზავნით." "არც ერთი ჩატი ჯერ არაა." + "რჩეულები" + "თქვენ შეგიძლიათ ოთახის რჩეულებში დამატება ოთახების პარამეტრებში. +ახლა კი შეგიძლიათ ფილტრების მოხსნა სხვა ოთახების გამოსაჩენად" + "თქვენ ჯერ არ გაქვთ რჩეული ჩატები" "მოწვევები" + "დაბალი პრიორიტეტი" + "თქვენ შეგიძლიათ წაშალოთ ფილტრები სხვა ჩეთების გამოსაჩენად" + "თქვენ არ გაქვთ ოთახები ამ არჩევნისთვის" "ხალხი" + "თქვენ ჯერ არ გაქვთ პირადი შეტყობინებები" + "ოთახები" + "თქვენ ჯერ არც ერთ ოთახში არ ხართ" + "წაუკითხავი" + "გილოცავთ! +თქვენ არ გაქვთ წაუკითხავი შეტყობინებები!" "ჩატები" + "წაკითხულად მონიშვნა" + "წაუკითხავად მონიშვნა" "როგორც ჩანს, ახალ მოწყობილობას იყენებთ. დაადასტურეთ სხვა მოწყობილობით თქვენს დაშიფრულ შეტყობინებებზე წვდომისთვის." "დაადასტურეთ, რომ ეს თქვენ ხართ" diff --git a/features/roomlist/impl/src/main/res/values-nb/translations.xml b/features/roomlist/impl/src/main/res/values-nb/translations.xml index ec671956cb..a97826a0c3 100644 --- a/features/roomlist/impl/src/main/res/values-nb/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nb/translations.xml @@ -1,13 +1,19 @@ + "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" "Skriv inn gjenopprettingsnøkkelen din" "Har du glemt din gjenopprettingsnøkkel?" + "For å sikre at du aldri går glipp av en viktig samtale, må du endre innstillingene dine for å tillate fullskjermvarsler når telefonen er låst." + "Forbedre samtaleopplevelsen din" "Er du sikker på at du vil takke nei til invitasjonen til å bli med i %1$s?" "Avvis invitasjon" "Er du sikker på at du vil avslå denne private chatten med %1$s?" "Avslå chat" "Ingen invitasjoner" "%1$s(%2$s) inviterte deg" + "Forespørsel om å bli med sendt" "Dette er en engangsprosess, takk for at du venter." "Setter opp kontoen din." "Opprett en ny samtale eller et nytt rom" @@ -28,7 +34,6 @@ Du har ingen uleste meldinger!" "Chatter" "Marker som lest" "Merk som ulest" - "Bla gjennom alle rom" "Det ser ut til at du bruker en ny enhet. Bekreft med en annen enhet for å få tilgang til de krypterte meldingene dine." "Bekreft at det er deg" diff --git a/features/roomlist/impl/src/main/res/values-nl/translations.xml b/features/roomlist/impl/src/main/res/values-nl/translations.xml index 3acfb82cf2..2b3462981b 100644 --- a/features/roomlist/impl/src/main/res/values-nl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-nl/translations.xml @@ -36,7 +36,6 @@ Je hebt geen ongelezen berichten!" "Chats" "Markeren als gelezen" "Markeren als ongelezen" - "Blader door alle kamers" "Het lijkt erop dat je een nieuw apparaat gebruikt. Verifieer met een ander apparaat om toegang te krijgen tot je versleutelde berichten." "Verifieer dat jij het bent" diff --git a/features/roomlist/impl/src/main/res/values-pl/translations.xml b/features/roomlist/impl/src/main/res/values-pl/translations.xml index c0721a6081..bc7f2cb189 100644 --- a/features/roomlist/impl/src/main/res/values-pl/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pl/translations.xml @@ -40,7 +40,6 @@ Nie masz żadnych nieprzeczytanych wiadomości!" "Wszystkie czaty" "Oznacz jako przeczytane" "Oznacz jako nieprzeczytane" - "Przeglądaj wszystkie pokoje" "Wygląda na to, że używasz nowego urządzenia. Zweryfikuj się innym urządzeniem, aby uzyskać dostęp do zaszyfrowanych wiadomości." "Potwierdź, że to Ty" diff --git a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml index 025a67ffcf..fa89f05d83 100644 --- a/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt-rBR/translations.xml @@ -29,7 +29,6 @@ Você não tem nenhuma mensagem não lida!" "Conversas" "Marcar como lido" "Marcar como não lido" - "Navegar por todas as salas" "Parece que você está usando um novo dispositivo. Verifique com outro dispositivo para acessar suas mensagens criptografadas." "Verifique se é você" diff --git a/features/roomlist/impl/src/main/res/values-pt/translations.xml b/features/roomlist/impl/src/main/res/values-pt/translations.xml index d5f11986e1..4d87f54b8b 100644 --- a/features/roomlist/impl/src/main/res/values-pt/translations.xml +++ b/features/roomlist/impl/src/main/res/values-pt/translations.xml @@ -40,7 +40,6 @@ Não tens nenhuma mensagem por ler!" "Conversas" "Marcar como lida" "Marcar como não lida" - "Consultar lista completa de salas" "Parece que estás a utilizar um novo dispositivo. Verifica-o com um outro para poderes aceder às tuas mensagens cifradas." "Verifica que és tu" diff --git a/features/roomlist/impl/src/main/res/values-ro/translations.xml b/features/roomlist/impl/src/main/res/values-ro/translations.xml index b4d276dd74..b332101f2c 100644 --- a/features/roomlist/impl/src/main/res/values-ro/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ro/translations.xml @@ -38,7 +38,6 @@ Nu aveți mesaje necitite!" "Toate conversatiile" "Marcați ca citită" "Marcați ca necitită" - "Răsfoiți toate camerele" "Se pare că folosiți un dispozitiv nou. Verificați-vă identitatea cu un alt dispozitiv pentru a accesa mesajele dumneavoastră criptate." "Verificați că sunteți dumneavoastră" diff --git a/features/roomlist/impl/src/main/res/values-ru/translations.xml b/features/roomlist/impl/src/main/res/values-ru/translations.xml index 63072a2dc6..c51fbc21d4 100644 --- a/features/roomlist/impl/src/main/res/values-ru/translations.xml +++ b/features/roomlist/impl/src/main/res/values-ru/translations.xml @@ -40,7 +40,6 @@ "Все чаты" "Пометить как прочитанное" "Отметить как непрочитанное" - "Просмотреть все комнаты" "Похоже, вы используете новое устройство. Чтобы получить доступ к зашифрованным сообщениям пройдите подтверждение с другим устройством." "Подтвердите, что это вы" diff --git a/features/roomlist/impl/src/main/res/values-sk/translations.xml b/features/roomlist/impl/src/main/res/values-sk/translations.xml index e2f67e643e..fb9da3707d 100644 --- a/features/roomlist/impl/src/main/res/values-sk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sk/translations.xml @@ -40,7 +40,6 @@ Nemáte žiadne neprečítané správy!" "Všetky konverzácie" "Označiť ako prečítané" "Označiť ako neprečítané" - "Prehliadať všetky miestnosti" "Vyzerá to tak, že používate nové zariadenie. Overte svoj prístup k zašifrovaným správam pomocou vášho druhého zariadenia." "Overte, že ste to vy" diff --git a/features/roomlist/impl/src/main/res/values-sv/translations.xml b/features/roomlist/impl/src/main/res/values-sv/translations.xml index 5509f17dc0..9c69cb1937 100644 --- a/features/roomlist/impl/src/main/res/values-sv/translations.xml +++ b/features/roomlist/impl/src/main/res/values-sv/translations.xml @@ -40,7 +40,6 @@ Du har inga olästa meddelanden!" "Alla chattar" "Markera som läst" "Markera som oläst" - "Bläddra bland alla rum" "Det verkar som om du använder en ny enhet. Verifiera med en annan enhet för att komma åt dina krypterade meddelanden." "Verifiera att det är du" diff --git a/features/roomlist/impl/src/main/res/values-tr/translations.xml b/features/roomlist/impl/src/main/res/values-tr/translations.xml index 1176b53662..1502b4fcaf 100644 --- a/features/roomlist/impl/src/main/res/values-tr/translations.xml +++ b/features/roomlist/impl/src/main/res/values-tr/translations.xml @@ -40,7 +40,6 @@ Okunmamış mesajınız yok!" "Sohbetler" "Okundu olarak işaretle" "Okunmamış olarak işaretle" - "Tüm odalara göz at" "Görünüşe göre yeni bir cihaz kullanıyorsunuz. Şifrelenmiş mesajlarınıza erişmek için başka bir cihazla doğrulayın." "Siz olduğunuzu doğrulayın" diff --git a/features/roomlist/impl/src/main/res/values-uk/translations.xml b/features/roomlist/impl/src/main/res/values-uk/translations.xml index 2f136bc64f..26875754d3 100644 --- a/features/roomlist/impl/src/main/res/values-uk/translations.xml +++ b/features/roomlist/impl/src/main/res/values-uk/translations.xml @@ -40,7 +40,6 @@ "Бесіди" "Позначити прочитаним" "Позначити непрочитаним" - "Переглянути всі кімнати" "Схоже, ви використовуєте новий пристрій. Щоб отримати доступ до зашифрованих повідомлень, підтвердьте особу за допомогою іншого пристрою." "Підтвердьте, що це ви" diff --git a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml index 7a6057fc0b..4a13d33c6c 100644 --- a/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh-rTW/translations.xml @@ -40,7 +40,6 @@ "所有聊天室" "標為已讀" "標為未讀" - "瀏覽所有聊天室" "您似乎正在使用新的裝置。請使用另一個裝置進行驗證,以存取您的加密訊息。" "驗證這是您本人" diff --git a/features/roomlist/impl/src/main/res/values-zh/translations.xml b/features/roomlist/impl/src/main/res/values-zh/translations.xml index b4970b4382..7c5d778ddf 100644 --- a/features/roomlist/impl/src/main/res/values-zh/translations.xml +++ b/features/roomlist/impl/src/main/res/values-zh/translations.xml @@ -40,7 +40,6 @@ "全部聊天" "标记为已读" "标记为未读" - "浏览所有聊天室" "您似乎正在使用新设备。使用另一台设备进行验证以访问您的加密消息。" "验证是你本人" diff --git a/features/roomlist/impl/src/main/res/values/localazy.xml b/features/roomlist/impl/src/main/res/values/localazy.xml index 2e1159c02c..d72af71f84 100644 --- a/features/roomlist/impl/src/main/res/values/localazy.xml +++ b/features/roomlist/impl/src/main/res/values/localazy.xml @@ -40,7 +40,6 @@ You don’t have any unread messages!" "Chats" "Mark as read" "Mark as unread" - "Browse all rooms" "Looks like you’re using a new device. Verify with another device to access your encrypted messages." "Verify it’s you" diff --git a/features/securebackup/impl/src/main/res/values-es/translations.xml b/features/securebackup/impl/src/main/res/values-es/translations.xml index 64a98d6f94..4d4413690b 100644 --- a/features/securebackup/impl/src/main/res/values-es/translations.xml +++ b/features/securebackup/impl/src/main/res/values-es/translations.xml @@ -1,44 +1,70 @@ - "Desactivar copia de seguridad" + "Borrar almacén de claves" "Activar copia de seguridad" - "La copia de seguridad garantiza que no pierdas tu historial de mensajes. %1$s." - "Copia de seguridad" + "Almacena tu identidad criptográfica y las claves de tus mensajes de forma segura en el servidor. Esto te permitirá ver tu historial de mensajes en cualquier dispositivo nuevo. %1$s." + "Almacenamiento de claves" + "El almacenamiento de claves debe estar activado para configurar la recuperación." + "Subir claves desde este dispositivo" + "Permitir almacenamiento de claves" "Cambiar la clave de recuperación" + "Recupera tu identidad criptográfica y tu historial de mensajes con una clave de recuperación si has perdido todos tus dispositivos actuales." "Introduzca la clave de recuperación" - "La copia de seguridad de tus chats no está sincronizada ahora mismo." + "Tu almacén de claves no está sincronizado actualmente." "Configurar la recuperación" "Accede a tus mensajes cifrados si pierdes todos tus dispositivos o cierras sesión de %1$s en cualquier lugar." + "Abre %1$s en un dispositivo de escritorio" + "Vuelve a iniciar sesión en tu cuenta" + "Cuando se te pida que verifiques tu dispositivo, selecciona %1$s" + "«Restablecer todo»" + "Sigue las instrucciones para crear una nueva clave de recuperación" + "Guarda tu nueva clave de recuperación en un administrador de contraseñas o en una nota cifrada" + "Restablece el cifrado de tu cuenta usando otro dispositivo" + "Continuar con el restablecimiento" + "Se conservarán los detalles de tu cuenta, tus contactos, tus preferencias y tu lista de chats" + "Perderás cualquier historial de mensajes que solo esté almacenado en el servidor" + "Tendrás que verificar de nuevo todos tus dispositivos y contactos existentes" + "Restablece tu identidad solo si no tienes acceso a otro dispositivo en el que hayas iniciado sesión y has perdido tu clave de recuperación." + "¿No puedes confirmar? Tendrás que restablecer tu identidad." "Desactivar" "Perderás tus mensajes cifrados si cierras sesión en todos los dispositivos." "¿Estás seguro de que quieres desactivar la copia de seguridad?" - "Desactivar la copia de seguridad eliminará la copia de seguridad de tu clave de encriptación actual y desactivará otras funciones de seguridad. En este caso:" + "Al borrar el almacén de claves, se eliminarán del servidor tu identidad criptográfica y claves de los mensajes, y se desactivarán las siguientes funciones de seguridad:" "No tendrás un historial de mensajes cifrados en nuevos dispositivos" - "Perderás el acceso a tus mensajes cifrados si cierras sesión %1$s en todas partes" - "¿Estás seguro de que quieres desactivar la copia de seguridad?" + "Perderás el acceso a tus mensajes cifrados si cierras sesión en %1$s en todas partes" + "¿Estás seguro de que quieres desactivar el almacenamiento de claves y borrarlo?" "Obtén una nueva clave de recuperación si has perdido la que tenías. Después de cambiar la clave de recuperación, la anterior dejará de funcionar." "Generar una nueva clave de recuperación" - "Asegúrate de que puedes guardar tu clave de recuperación en algún lugar seguro" + "¡No la compartas con nadie!" "Clave de recuperación cambiada" "¿Cambiar la clave de recuperación?" - "Introduce tu clave de recuperación para confirmar el acceso a la copia de seguridad de tú chat." - "Por favor, inténtalo de nuevo para confirmar el acceso a tu copia de seguridad del chat." + "Crear nueva clave de recuperación" + "¡Asegúrate de que nadie pueda ver esta pantalla!" + "Inténtalo de nuevo para confirmar el acceso a tu almacén de claves." "Clave de recuperación incorrecta" - "Introduce el código de 48 caracteres." - "Ingresar…" + "Si tienes una clave o frase de seguridad, también funcionará." + "Introducir…" + "¿Perdiste tu clave de recuperación?" "Clave de recuperación confirmada" + "Introduce tu clave de recuperación" "Clave de recuperación copiada" "Generando…" "Guardar clave de recuperación" - "Anota tu clave de recuperación en un lugar seguro o guárdala en un administrador de contraseñas." + "Anota esta clave de recuperación en un lugar seguro, como un administrador de contraseñas, una nota cifrada o una caja fuerte física." "Pulsa para copiar la clave de recuperación" "Guardar tu clave de recuperación" "No podrás acceder a tu nueva clave de recuperación después de este paso." "¿Has guardado tu clave de recuperación?" - "La copia de seguridad del chat está protegida por una clave de recuperación. Si necesitas una nueva clave de recuperación después de la configuración, puedes volver a crearla seleccionando «Cambiar clave de recuperación»." + "Tu almacén de claves está protegido por una clave de recuperación. Si necesitas una nueva clave de recuperación después de la configuración, puedes volver a crearla seleccionando «Cambiar la clave de recuperación»." "Generar tu clave de recuperación" - "Asegúrate de que puedes guardar tu clave de recuperación en algún lugar seguro" + "¡No la compartas con nadie!" "Configuración de recuperación terminada" "Configurar la recuperación" - "Ingresar…" + "Sí, restablecer ahora" + "Este proceso es irreversible." + "¿Estás seguro de que quieres restablecer tu identidad?" + "Se ha producido un error desconocido. Comprueba que la contraseña de tu cuenta sea correcta y vuelve a intentarlo." + "Introducir…" + "Confirma que quieres restablecer tu identidad." + "Introduce la contraseña de tu cuenta para continuar" diff --git a/features/securebackup/impl/src/main/res/values-eu/translations.xml b/features/securebackup/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..4b26d8692e --- /dev/null +++ b/features/securebackup/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,56 @@ + + + "Ezabatu gakoen biltegia" + "Aktibatu babeskopia" + "Gorde zure identitate-kriptografikoa eta mezuen gakoak modu seguruan zerbitzarian. Horrek zure mezuen historia edozein gailu berritan ikusteko aukera emango dizu. %1$s." + "Gakoen biltegiratzea" + "Aldatu berreskuratze-gakoa" + "Sartu berreskuratze-gakoa" + "Zure giltzen-biltegiratzea gaur-gaurkoz sinkronizatu gabe dago." + "Konfiguratu berreskurapena" + "Ireki %1$s mahaigaineko gailu batean" + "Hasi saioa berriro zure kontuan" + "Gailua egiaztatzeko eskatzen zaizunean, hautatu %1$s" + "\"Berrezarri guztia\"" + "Jarraitu argibideak berreskuratze-gako berri bat sortzeko" + "Gorde berreskuratze-gako berria pasahitz-kudeatzaile batean edo enkriptatutako ohar batean" + "Berrezarri zure kontuaren enkriptazioa beste gailu bat erabiliz" + "Ezin duzu baieztatu? Zure identitatea berrezarri beharko duzu." + "Desaktibatu" + "Enkriptatutako mezuak galduko dituzu gailu guztietan saioa amaitzen baduzu." + "Ziur babeskopia desaktibatu nahi duzula?" + "Gakoen biltegiratzea ezabatuz gero, zure identitate kriptografikoa eta mezuen gakoak zerbitzaritik kenduko dira eta honako segurtasun ezaugarriak desaktibatu egingo dira:" + "Aurrerantzean ez duzu mezuen historia enkriptatuta izango gailu berrietan" + "Gailu guztietan amaitzen baduzu %1$s saioa, enkriptatutako mezuetarako sarbidea galduko duzu" + "Ziur gakoen biltegia desaktibatu eta ezabatu nahi duzula?" + "Lortu berreskuratze-gako berri bat lehendik duzuna galdu baduzu. Berreskuratze-gakoa aldatu ondoren, zaharrak ez du funtzionatuko." + "Sortu berreskuratze-gako berri bat" + "Ez partekatu inorekin!" + "Berreskuratze-gakoa aldatu da" + "Berreskuratze-gakoa aldatu?" + "Sortu berreskuratze-gako berria" + "Egiaztatu inork ezin duela pantaila hau ikusi!" + "Berreskuratze-gako okerra" + "Segurtasun-gako edo -esaldi bat baduzu, honek ere balio du." + "Sartu…" + "Berreskuratze-gakoa galdu duzu?" + "Berreskuratze-gakoa berretsi da" + "Berreskuratze-gakoa kopiatu da" + "Sortzen…" + "Gorde berreskuratze-gakoa" + "Idatzi berreskuratze-gako hau leku seguru batean, esate baterako, pasahitzen kudeatzaile, enkriptatutako ohar, edo kutxa gotor fisiko batean." + "Sakatu berreskuratze-gakoa kopiatzeko" + "Gorde berreskuratze-gakoa leku seguru batean" + "Ezingo duzu berreskuratze-gako berria atzitu urrats honen ondoren." + "Gorde al duzu berreskuratze-gakoa?" + "Sortu berreskuratze-gakoa" + "Ez partekatu inorekin!" + "Berreskuratzea ondo konfiguratu da" + "Konfiguratu berreskurapena" + "Bai, berrezarri orain" + "Ezin da desegin." + "Ziur zure identitatea berrezarri nahi duzula?" + "Sartu…" + "Berretsi zure identitatea berrezarri nahi duzula." + "Idatzi kontuaren pasahitza aurrera egiteko" + diff --git a/features/securebackup/impl/src/main/res/values-ka/translations.xml b/features/securebackup/impl/src/main/res/values-ka/translations.xml index 61e4c51c1e..3e17cb41a6 100644 --- a/features/securebackup/impl/src/main/res/values-ka/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ka/translations.xml @@ -5,9 +5,13 @@ "სარეზერვო ასლი უზრუნველყოფს იმას, რომ თქვენ შეტყობინებების ისტორიას არ დაკარგავთ. %1$s" "გასაღების საცავი" "აღდგენის გასაღების შეცვლა" + "შეიყვანეთ აღდგენის გასაღები" "თქვენი ჩატის სარეზერვო ასლი ამჟამად არ არის სინქრონიზებული." "აღდგენის დაყენება" "მიიღეთ წვდომა თქვენს დაშიფრულ შეტყობინებებზე, თუ დაკარგავთ თქვენს ყველა მოწყობილობას ან გამოხვალთ სისტემიდან %1$s-დან ყველგან." + "გახსენით %1$s კომპიუტერზე" + "შედით თქვენს ანგარიშში კიდევ ერთხელ" + "გაანულეთ თქვენი ანგარიშის დაშიფვრა სხვა მოწყობილობის დახმარებით" "გამორთვა" "თქვენ დაკარგავთ დაშიფრულ შეტყობინებებს, თუ ყველა მოწყობილობიდან გამოხვალთ." "დარწმუნებული ხართ, რომ გსურთ გამორთოთ სარეზერვო ასლი?" @@ -17,11 +21,11 @@ "დარწმუნებული ხართ, რომ გსურთ გამორთოთ სარეზერვო ასლი?" "მიიღეთ ახალი აღდგენის გასაღები, თუ დაკარგეთ არსებული. აღდგენის გასაღების შეცვლის შემდეგ, ძველი აღარ იმუშავებს." "ახალი აღდგენის გასაღების შექმნა" - "დარწმუნდით, რომ შეგიძლიათ შეინახოთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას" + "არავის გაუზიაროთ!" "აღდგენის გასაღები შეიცვალა" "გსურთ აღდგენის გასაღების შეცვლა?" "დარწმუნდით, რომ ვერავინ ხედავს ამ ეკრანს!" - "გთხოვთ, სცადოთ ხელახლა, რათა თქვენი ჩეთის სარეზერვო ასლაზე წვდომა დაადასტუროთ" + "გთხოვთ, სცადოთ წვდომის დადასტურება გასაღებების დამგროვებელთან კვლავ." "აღდგენის არასწორი გასაღები" "თუ თქვენ გაქვთ უსაფრთხოების გასაღები ან უსაფრთხოების ფრაზა, ეს ასევე იმუშავებს." "შეყვანა" @@ -29,14 +33,14 @@ "დაკოპირებულია აღდგენის გასაღები" "გენერირება…" "აღდგენის გასაღების შენახვა" - "ჩაწერეთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას ან შეინახეთ პაროლის მენეჯერში." + "ჩაწერეთ ეს აღდგენის გასაღები სადმე უსაფრთხო ადგილას, მაგალითად პაროლების მენეჯერში, დაშიფრულ შენიშვნაში ან ფიზიკურ სეიფში." "აღდგენის გასაღების დასაკოპირებლად, დააწკაპუნეთ" "შეინახეთ აღდგენის გასაღები" "თქვენ ვერ შეძლებთ წვდომას თქვენი ახალი აღდგენის გასაღებზე ამ ნაბიჯის შემდეგ." "შეინახეთ თქვენი აღდგენის გასაღები?" "თქვენი ჩატის სარეზერვო ასლი დაცულია აღდგენის გასაღებით. თუ დაყენების შემდეგ გჭირდებათ ახალი აღდგენის გასაღები, შეგიძლიათ ხელახლა შექმნათ „აღდგენის გასაღების შეცვლის“ არჩევით." "შექმენით აღდგენის გასაღები" - "დარწმუნდით, რომ შეგიძლიათ შეინახოთ თქვენი აღდგენის გასაღები სადმე უსაფრთხო ადგილას" + "არავის გაუზიაროთ!" "აღდგენის დაყენება წარმატებით დასრულდა" "აღდგენის დაყენება" "შეყვანა" diff --git a/features/securebackup/impl/src/main/res/values-nb/translations.xml b/features/securebackup/impl/src/main/res/values-nb/translations.xml index cbfe989a0b..7d7401473e 100644 --- a/features/securebackup/impl/src/main/res/values-nb/translations.xml +++ b/features/securebackup/impl/src/main/res/values-nb/translations.xml @@ -2,9 +2,18 @@ "Slå på sikkerhetskopiering" "Lagre din kryptografiske identitet og meldingsnøkler sikkert på serveren. Dette gjør at du kan se meldingshistorikken din på alle nye enheter. %1$s." + "Last opp nøkler fra denne enheten" "Endre gjenopprettingsnøkkel" + "Gjenopprett din kryptografiske identitet og meldingshistorikk med en gjenopprettingsnøkkel hvis du har mistet alle dine brukte enheter." "Skriv inn gjenopprettingsnøkkel" + "Konfigurer gjenoppretting" "Få tilgang til de krypterte meldingene dine hvis du mister alle enhetene dine eller blir logget ut av %1$s overalt." + "Logg på kontoen din igjen" + "Når du blir bedt om å verifisere din enhet, velger du %1$s" + "Tilbakestill alt" + "Følg instruksjonene for å opprette en ny gjenopprettingsnøkkel" + "Du bør lagre din nye gjenopprettingsnøkkel i en passordbehandler eller i et kryptert notat" + "Tilbakestill krypteringen for kontoen din ved hjelp av en annen enhet" "Fortsett tilbakestillingen" "Dine kontodetaljer, kontakter, innstillinger og chatteliste vil bli beholdt" "Du mister all meldingshistorikk som bare er lagret på serveren" @@ -12,6 +21,38 @@ "Tilbakestill identiteten din bare hvis du ikke har tilgang til en annen pålogget enhet og du har mistet gjenopprettingsnøkkelen." "Kan du ikke bekrefte? Du må tilbakestille identiteten din." "Slå av" + "Du mister de krypterte meldingene dine hvis du er logget ut av alle enheter." + "Er du sikker på at du vil slå av sikkerhetskopiering?" "Du vil ikke ha kryptert meldingshistorikk på nye enheter" + "Du mister tilgangen til de krypterte meldingene dine hvis du er logget ut av %1$s overalt" + "Få en ny gjenopprettingsnøkkel hvis du har mistet den eksisterende. Etter at du har endret gjenopprettingsnøkkelen, vil den gamle ikke lenger fungere." + "Generer en ny gjenopprettingsnøkkel" + "Ikke del dette med noen!" + "Gjenopprettingsnøkkel endret" + "Endre gjenopprettingsnøkkelen?" + "Opprett ny gjenopprettingsnøkkel" + "Sørg for at ingen kan se denne skjermen!" + "Feil gjenopprettingsnøkkel" + "Hvis du har en sikkerhetsnøkkel eller sikkerhetsfrase, vil dette også fungere." + "Har du mistet gjenopprettingsnøkkelen?" + "Gjenopprettingsnøkkel bekreftet" "Skriv inn gjenopprettingsnøkkelen din" + "Kopiert gjenopprettingsnøkkel" + "Genererer…" + "Lagre gjenopprettingsnøkkelen" + "Skriv ned denne gjenopprettingsnøkkelen på et trygt sted, for eksempel i en passordbehandler, på en kryptert lapp eller i en fysisk safe." + "Trykk for å kopiere gjenopprettingsnøkkelen" + "Lagre gjenopprettingsnøkkelen på et trygt sted" + "Du vil ikke ha tilgang til den nye gjenopprettingsnøkkelen etter dette trinnet." + "Har du lagret gjenopprettingsnøkkelen din?" + "Generer gjenopprettingsnøkkelen din" + "Ikke del dette med noen!" + "Gjenopprettingsoppsett vellykket" + "Konfigurer gjenoppretting" + "Ja, tilbakestill nå" + "Denne prosessen kan ikke angres." + "Er du sikker på at du vil tilbakestille identiteten din?" + "En ukjent feil har oppstått. Vennligst sjekk at passordet ditt er riktig og prøv igjen." + "Bekreft at du vil tilbakestille identiteten din." + "Skriv inn passordet til kontoen din for å fortsette" diff --git a/features/signedout/impl/src/main/res/values-eu/translations.xml b/features/signedout/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..678a775fad --- /dev/null +++ b/features/signedout/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,8 @@ + + + "Pasahitza beste saio batean aldatu duzu" + "Saioa beste saio batetik ezabatu duzu" + "Zerbitzariaren administratzaileak zure sarbidea baliogabetu du" + "Litekeena da saioa amaitu izana ondorengo arrazoietako bat dela eta. Hasi saioa berriro %s erabiltzen jarraitzeko." + "Saioa amaitu duzu" + diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt index 77917e3106..b7b7ba2561 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEvents.kt @@ -14,4 +14,5 @@ sealed interface UserProfileEvents { data class UnblockUser(val needsConfirmation: Boolean = false) : UserProfileEvents data object ClearBlockUserError : UserProfileEvents data object ClearConfirmationDialog : UserProfileEvents + data object WithdrawVerification : UserProfileEvents } diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt index 5d3ae9c132..f32033b0a7 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileState.kt @@ -16,7 +16,7 @@ data class UserProfileState( val userId: UserId, val userName: String?, val avatarUrl: String?, - val isVerified: AsyncData, + val verificationState: UserProfileVerificationState, val isBlocked: AsyncData, val startDmActionState: AsyncAction, val displayConfirmationDialog: ConfirmationDialog?, @@ -30,3 +30,10 @@ data class UserProfileState( Unblock } } + +enum class UserProfileVerificationState { + UNKNOWN, + VERIFIED, + UNVERIFIED, + VERIFICATION_VIOLATION, +} diff --git a/features/userprofile/impl/build.gradle.kts b/features/userprofile/impl/build.gradle.kts index 2c94082d0c..70769c986d 100644 --- a/features/userprofile/impl/build.gradle.kts +++ b/features/userprofile/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.mediaviewer.api) implementation(projects.features.call.api) + implementation(projects.features.verifysession.api) api(projects.features.userprofile.api) api(projects.features.userprofile.shared) implementation(libs.coil.compose) 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 489d82480c..9f40fa4f6d 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 @@ -25,6 +25,7 @@ import io.element.android.features.call.api.ElementCallEntryPoint import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.userprofile.impl.root.UserProfileNode import io.element.android.features.userprofile.shared.UserProfileNodeHelper +import io.element.android.features.verifysession.api.VerifySessionEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.createNode @@ -32,7 +33,9 @@ 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.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 @@ -43,6 +46,7 @@ class UserProfileFlowNode @AssistedInject constructor( private val elementCallEntryPoint: ElementCallEntryPoint, private val sessionIdHolder: CurrentSessionIdHolder, private val mediaViewerEntryPoint: MediaViewerEntryPoint, + private val verifySessionEntryPoint: VerifySessionEntryPoint, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -57,8 +61,13 @@ class UserProfileFlowNode @AssistedInject constructor( @Parcelize data class AvatarPreview(val name: String, val avatarUrl: String) : NavTarget + + @Parcelize + data class VerifyUser(val userId: UserId) : NavTarget } + private val inputs = inputs() + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { @@ -74,8 +83,12 @@ class UserProfileFlowNode @AssistedInject constructor( override fun onStartCall(dmRoomId: RoomId) { elementCallEntryPoint.startCall(CallType.RoomCall(sessionId = sessionIdHolder.current, roomId = dmRoomId)) } + + override fun onVerifyUser(userId: UserId) { + backstack.push(NavTarget.VerifyUser(userId)) + } } - val params = UserProfileNode.UserProfileInputs(userId = inputs().userId) + val params = UserProfileNode.UserProfileInputs(userId = inputs.userId) createNode(buildContext, listOf(callback, params)) } is NavTarget.AvatarPreview -> { @@ -96,6 +109,15 @@ class UserProfileFlowNode @AssistedInject constructor( .callback(callback) .build() } + is NavTarget.VerifyUser -> { + val params = VerifySessionEntryPoint.Params( + showDeviceVerifiedScreen = false, + verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId) + ) + verifySessionEntryPoint.nodeBuilder(this, buildContext) + .params(params) + .build() + } } } 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 1b99e8409b..f10fd48a04 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 @@ -42,7 +42,7 @@ class UserProfileNode @AssistedInject constructor( private val inputs = inputs() private val callback = inputs() - private val presenter = presenterFactory.create(inputs.userId) + private val presenter = presenterFactory.create(userId = inputs.userId) private val userProfileNodeHelper = UserProfileNodeHelper(inputs.userId) init { @@ -75,6 +75,7 @@ class UserProfileNode @AssistedInject constructor( onOpenDm = ::onStartDM, onStartCall = callback::onStartCall, openAvatarPreview = callback::openAvatarPreview, + onVerifyClick = callback::onVerifyUser, ) } } 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 8189f75aa3..4216287a70 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 @@ -24,10 +24,10 @@ import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog +import io.element.android.features.userprofile.api.UserProfileVerificationState 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.runCatchingUpdatingState import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId @@ -73,10 +73,8 @@ class UserProfilePresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() val isCurrentUser = remember { client.isMe(userId) } var confirmationDialog by remember { mutableStateOf(null) } - var userProfile by remember { mutableStateOf(null) } val startDmActionState: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val isBlocked: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } - val isVerified: MutableState> = remember { mutableStateOf(AsyncData.Uninitialized) } val dmRoomId by getDmRoomId() val canCall by getCanCall(dmRoomId) LaunchedEffect(Unit) { @@ -86,14 +84,7 @@ class UserProfilePresenter @AssistedInject constructor( .onEach { isBlocked.value = AsyncData.Success(it) } .launchIn(this) } - LaunchedEffect(Unit) { - userProfile = client.getProfile(userId).getOrNull() - } - LaunchedEffect(Unit) { - suspend { - client.encryptionService().isUserVerified(userId).getOrThrow() - }.runCatchingUpdatingState(isVerified) - } + val userProfile by produceState(null) { value = client.getProfile(userId).getOrNull() } fun handleEvents(event: UserProfileEvents) { when (event) { @@ -129,6 +120,8 @@ class UserProfilePresenter @AssistedInject constructor( UserProfileEvents.ClearStartDMState -> { startDmActionState.value = AsyncAction.Uninitialized } + // Do nothing for withdrawing verification as it's handled by the RoomMemberDetailsPresenter if needed + UserProfileEvents.WithdrawVerification -> Unit } } @@ -137,7 +130,7 @@ class UserProfilePresenter @AssistedInject constructor( userName = userProfile?.displayName, avatarUrl = userProfile?.avatarUrl, isBlocked = isBlocked.value, - isVerified = isVerified.value, + verificationState = UserProfileVerificationState.UNKNOWN, startDmActionState = startDmActionState.value, displayConfirmationDialog = confirmationDialog, isCurrentUser = isCurrentUser, diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index 98140fdd00..5a843bad81 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -18,12 +18,14 @@ import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.impl.root.UserProfilePresenter import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData 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.UserId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -69,7 +71,7 @@ class UserProfilePresenterTest { assertThat(initialState.userName).isEqualTo(matrixUser.displayName) assertThat(initialState.avatarUrl).isEqualTo(matrixUser.avatarUrl) assertThat(initialState.isBlocked).isEqualTo(AsyncData.Success(false)) - assertThat(initialState.isVerified.dataOrNull()).isFalse() + assertThat(initialState.verificationState).isEqualTo(UserProfileVerificationState.UNKNOWN) assertThat(initialState.dmRoomId).isEqualTo(A_ROOM_ID) assertThat(initialState.canCall).isFalse() } @@ -361,36 +363,25 @@ class UserProfilePresenterTest { } } - @Test - fun `present - when user is verified, the value in the state is true`() = runTest { - val client = createFakeMatrixClient(isUserVerified = true) - val presenter = createUserProfilePresenter( - client = client, - ) - presenter.test { - assertThat(awaitItem().isVerified.isUninitialized()).isTrue() - assertThat(awaitItem().isVerified.isLoading()).isTrue() - assertThat(awaitItem().isVerified.dataOrNull()).isTrue() - } - } - private suspend fun ReceiveTurbine.awaitFirstItem(): T { - skipItems(2) + skipItems(1) return awaitItem() } private fun createFakeMatrixClient( - isUserVerified: Boolean = false, + isUserVerified: Boolean = true, + userIdentityState: IdentityState? = null, ignoreUserResult: (UserId) -> Result = { Result.success(Unit) }, unIgnoreUserResult: (UserId) -> Result = { Result.success(Unit) }, ignoredUsersFlow: StateFlow> = MutableStateFlow(persistentListOf()) ) = FakeMatrixClient( encryptionService = FakeEncryptionService( - isUserVerifiedResult = { Result.success(isUserVerified) } + isUserVerifiedResult = { Result.success(isUserVerified) }, + getUserIdentityResult = { Result.success(userIdentityState) } ), ignoreUserResult = ignoreUserResult, unIgnoreUserResult = unIgnoreUserResult, - ignoredUsersFlow = ignoredUsersFlow + ignoredUsersFlow = ignoredUsersFlow, ) private fun createUserProfilePresenter( @@ -401,7 +392,7 @@ class UserProfilePresenterTest { return UserProfilePresenter( userId = userId, client = client, - startDMAction = startDMAction + startDMAction = startDMAction, ) } } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt index c8755aac56..3df57b9eb0 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt @@ -22,7 +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.architecture.AsyncData +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.libraries.designsystem.atomic.atoms.MatrixBadgeAtom import io.element.android.libraries.designsystem.atomic.molecules.MatrixBadgeRowMolecule import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -30,6 +30,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ButtonSize +import io.element.android.libraries.designsystem.theme.components.OutlinedButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.testtags.TestTags @@ -42,8 +44,9 @@ fun UserProfileHeaderSection( avatarUrl: String?, userId: UserId, userName: String?, - isUserVerified: AsyncData, + verificationState: UserProfileVerificationState, openAvatarPreview: (url: String) -> Unit, + withdrawVerificationClick: () -> Unit, modifier: Modifier = Modifier ) { Column( @@ -74,16 +77,37 @@ fun UserProfileHeaderSection( color = ElementTheme.colors.textSecondary, textAlign = TextAlign.Center, ) - if (isUserVerified.dataOrNull() == true) { - MatrixBadgeRowMolecule( - data = listOf( - MatrixBadgeAtom.MatrixBadgeData( - text = stringResource(CommonStrings.common_verified), - icon = CompoundIcons.Verified(), - type = MatrixBadgeAtom.Type.Positive, - ) - ).toImmutableList(), - ) + when (verificationState) { + UserProfileVerificationState.UNKNOWN, UserProfileVerificationState.UNVERIFIED -> Unit + UserProfileVerificationState.VERIFIED -> { + MatrixBadgeRowMolecule( + data = listOf( + MatrixBadgeAtom.MatrixBadgeData( + text = stringResource(CommonStrings.common_verified), + icon = CompoundIcons.Verified(), + type = MatrixBadgeAtom.Type.Positive, + ) + ).toImmutableList(), + ) + } + UserProfileVerificationState.VERIFICATION_VIOLATION -> { + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(CommonStrings.crypto_identity_change_profile_pin_violation, userName ?: userId.value), + color = ElementTheme.colors.textCriticalPrimary, + style = ElementTheme.typography.fontBodyMdMedium, + ) + + Spacer(modifier = Modifier.height(16.dp)) + + OutlinedButton( + modifier = Modifier.fillMaxWidth(), + size = ButtonSize.MediumLowPadding, + text = stringResource(CommonStrings.crypto_identity_change_withdraw_verification_action), + onClick = withdrawVerificationClick, + ) + } } Spacer(Modifier.height(40.dp)) } @@ -96,7 +120,21 @@ internal fun UserProfileHeaderSectionPreview() = ElementPreview { avatarUrl = null, userId = UserId("@alice:example.com"), userName = "Alice", - isUserVerified = AsyncData.Success(true), + verificationState = UserProfileVerificationState.VERIFIED, openAvatarPreview = {}, + withdrawVerificationClick = {}, + ) +} + +@PreviewsDayNight +@Composable +internal fun UserProfileHeaderSectionWithVerificationViolationPreview() = ElementPreview { + UserProfileHeaderSection( + avatarUrl = null, + userId = UserId("@alice:example.com"), + userName = "Alice", + verificationState = UserProfileVerificationState.VERIFICATION_VIOLATION, + openAvatarPreview = {}, + withdrawVerificationClick = {}, ) } diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt index deb72a5bab..61f4669769 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileNodeHelper.kt @@ -24,6 +24,7 @@ class UserProfileNodeHelper( fun openAvatarPreview(username: String, avatarUrl: String) fun onStartDM(roomId: RoomId) fun onStartCall(dmRoomId: RoomId) + fun onVerifyUser(userId: UserId) } fun onShareUser( diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt index a53f53c48c..be9fad9c94 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileStateProvider.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.RoomId @@ -22,13 +23,14 @@ open class UserProfileStateProvider : PreviewParameterProvider get() = sequenceOf( aUserProfileState(), aUserProfileState(userName = null), - aUserProfileState(isBlocked = AsyncData.Success(true), isVerified = AsyncData.Success(true)), + aUserProfileState(isBlocked = AsyncData.Success(true), verificationState = UserProfileVerificationState.VERIFIED), aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Block), aUserProfileState(displayConfirmationDialog = UserProfileState.ConfirmationDialog.Unblock), - aUserProfileState(isBlocked = AsyncData.Loading(true), isVerified = AsyncData.Loading()), + aUserProfileState(isBlocked = AsyncData.Loading(true), verificationState = UserProfileVerificationState.UNKNOWN), aUserProfileState(startDmActionState = AsyncAction.Loading), aUserProfileState(canCall = true), aUserProfileState(startDmActionState = ConfirmingStartDmWithMatrixUser(aMatrixUser())), + aUserProfileState(verificationState = UserProfileVerificationState.VERIFICATION_VIOLATION), ) } @@ -37,7 +39,7 @@ fun aUserProfileState( userName: String? = "Daniel", avatarUrl: String? = null, isBlocked: AsyncData = AsyncData.Success(false), - isVerified: AsyncData = AsyncData.Success(false), + verificationState: UserProfileVerificationState = UserProfileVerificationState.UNVERIFIED, startDmActionState: AsyncAction = AsyncAction.Uninitialized, displayConfirmationDialog: UserProfileState.ConfirmationDialog? = null, isCurrentUser: Boolean = false, @@ -49,7 +51,7 @@ fun aUserProfileState( userName = userName, avatarUrl = avatarUrl, isBlocked = isBlocked, - isVerified = isVerified, + verificationState = verificationState, startDmActionState = startDmActionState, displayConfirmationDialog = displayConfirmationDialog, isCurrentUser = isCurrentUser, diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt index dee44377d6..0c544cf659 100644 --- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt +++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileView.kt @@ -24,6 +24,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection import io.element.android.libraries.designsystem.components.async.AsyncActionView @@ -38,6 +39,7 @@ 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.core.UserId import io.element.android.libraries.matrix.ui.components.CreateDmConfirmationBottomSheet import io.element.android.libraries.ui.strings.CommonStrings @@ -50,6 +52,7 @@ fun UserProfileView( onStartCall: (RoomId) -> Unit, goBack: () -> Unit, openAvatarPreview: (username: String, url: String) -> Unit, + onVerifyClick: (UserId) -> Unit, modifier: Modifier = Modifier, ) { Scaffold( @@ -68,10 +71,11 @@ fun UserProfileView( avatarUrl = state.avatarUrl, userId = state.userId, userName = state.userName, - isUserVerified = state.isVerified, + verificationState = state.verificationState, openAvatarPreview = { avatarUrl -> openAvatarPreview(state.userName ?: state.userId.value, avatarUrl) }, + withdrawVerificationClick = { state.eventSink(UserProfileEvents.WithdrawVerification) }, ) UserProfileMainActionsSection( isCurrentUser = state.isCurrentUser, @@ -82,7 +86,7 @@ fun UserProfileView( ) Spacer(modifier = Modifier.height(26.dp)) if (!state.isCurrentUser) { - VerifyUserSection(state) + VerifyUserSection(state, onVerifyClick = { onVerifyClick(state.userId) }) BlockUserSection(state) BlockUserDialogs(state) } @@ -116,14 +120,15 @@ fun UserProfileView( } @Composable -private fun VerifyUserSection(state: UserProfileState) { - if (state.isVerified.dataOrNull() == false) { +private fun VerifyUserSection( + state: UserProfileState, + onVerifyClick: () -> Unit, +) { + if (state.verificationState == UserProfileVerificationState.UNVERIFIED) { ListItem( - headlineContent = { Text(stringResource(CommonStrings.common_verify_identity)) }, - supportingContent = { Text(stringResource(R.string.screen_room_member_details_verify_button_subtitle)) }, + headlineContent = { Text(stringResource(CommonStrings.common_verify_user)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())), - enabled = false, - onClick = { }, + onClick = onVerifyClick, ) } } @@ -139,6 +144,7 @@ internal fun UserProfileViewPreview( goBack = {}, onOpenDm = {}, onStartCall = {}, - openAvatarPreview = { _, _ -> } + openAvatarPreview = { _, _ -> }, + onVerifyClick = {}, ) } diff --git a/features/userprofile/shared/src/main/res/values-es/translations.xml b/features/userprofile/shared/src/main/res/values-es/translations.xml index 7d9a03375e..c65165c04f 100644 --- a/features/userprofile/shared/src/main/res/values-es/translations.xml +++ b/features/userprofile/shared/src/main/res/values-es/translations.xml @@ -9,8 +9,11 @@ "Bloquear" "Los usuarios bloqueados no podrán enviarte mensajes y todos sus mensajes se ocultarán. Puedes desbloquearlos cuando quieras." "Bloquear usuario" + "Perfil" "Desbloquear" "Podrás ver todos sus mensajes de nuevo." "Desbloquear usuario" + "Utiliza la aplicación web para verificar a este usuario." + "Verificar a %1$s" "Se ha producido un error al intentar iniciar un chat" diff --git a/features/userprofile/shared/src/main/res/values-eu/translations.xml b/features/userprofile/shared/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..42050a53ae --- /dev/null +++ b/features/userprofile/shared/src/main/res/values-eu/translations.xml @@ -0,0 +1,19 @@ + + + "Blokeatu" + "Blokeatutako erabiltzaileek ezingo dizute mezurik bidali eta beren mezuak ezkutatuko dira. Edozein unetan desblokeatu ditzakezu." + "Blokeatu erabiltzailea" + "Desblokeatu" + "Beraien mezu guztiak berriro ikusteko aukera izango duzu." + "Desblokeatu erabiltzailea" + "Blokeatu" + "Blokeatutako erabiltzaileek ezingo dizute mezurik bidali eta beren mezuak ezkutatuko dira. Edozein unetan desblokeatu ditzakezu." + "Blokeatu erabiltzailea" + "Profila" + "Desblokeatu" + "Beraien mezu guztiak berriro ikusteko aukera izango duzu." + "Desblokeatu erabiltzailea" + "Erabili web-aplikazioa erabiltzaile hau egiaztatzeko." + "Egiaztatu %1$s" + "Errorea gertatu da txata hasten saiatzean" + diff --git a/features/userprofile/shared/src/main/res/values-ka/translations.xml b/features/userprofile/shared/src/main/res/values-ka/translations.xml index f0b863e201..ca90f0ea34 100644 --- a/features/userprofile/shared/src/main/res/values-ka/translations.xml +++ b/features/userprofile/shared/src/main/res/values-ka/translations.xml @@ -9,6 +9,7 @@ "დაბლოკვა" "დაბლოკილი მომხმარებლები ვერ შეძლებენ თქვენთვის შეტყობინების გაგზავნას და ყველა მათი შეტყობინება თქვენთვის დამალული იქნება. თქვენ მათი განბლოკვა ნებისმეირ დროს შეგიძლიათ." "მომხმარებლის დაბლოკვა" + "პროფილი" "განბლოკვა" "თქვენ კვლავ შეძლებთ მათგან ყველა შეტყობინების ნახვას." "Მომხმარებლის განბლოკვა" diff --git a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt index 6c81acd00a..0e614e53d7 100644 --- a/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt +++ b/features/userprofile/shared/src/test/kotlin/io/element/android/features/userprofile/UserProfileViewTest.kt @@ -15,13 +15,16 @@ import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileState +import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.R import io.element.android.features.userprofile.shared.UserProfileView import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_ROOM_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.testtags.TestTags import io.element.android.libraries.ui.strings.CommonStrings @@ -193,6 +196,17 @@ class UserProfileViewTest { rule.clickOn(CommonStrings.action_cancel) eventsRecorder.assertSingle(UserProfileEvents.ClearConfirmationDialog) } + + @Test + fun `on verify user clicked - the right callback is called`() = runTest { + ensureCalledOnceWithParam(A_USER_ID) { callback -> + rule.setUserProfileView( + state = aUserProfileState(userId = A_USER_ID, verificationState = UserProfileVerificationState.UNVERIFIED), + onVerifyClick = callback, + ) + rule.clickOn(CommonStrings.common_verify_user) + } + } } private fun AndroidComposeTestRule.setUserProfileView( @@ -202,6 +216,7 @@ private fun AndroidComposeTestRule.setUserP onShareUser: () -> Unit = EnsureNeverCalled(), onDmStarted: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onStartCall: (RoomId) -> Unit = EnsureNeverCalledWithParam(), + onVerifyClick: (UserId) -> Unit = EnsureNeverCalledWithParam(), goBack: () -> Unit = EnsureNeverCalled(), openAvatarPreview: (String, String) -> Unit = EnsureNeverCalledWithTwoParams(), ) { @@ -213,6 +228,7 @@ private fun AndroidComposeTestRule.setUserP onStartCall = onStartCall, goBack = goBack, openAvatarPreview = openAvatarPreview, + onVerifyClick = onVerifyClick, ) } } diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt index 2d2a789b3a..9d90f33e8d 100644 --- a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt +++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/IncomingVerificationEntryPoint.kt @@ -12,11 +12,11 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs -import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.verification.VerificationRequest interface IncomingVerificationEntryPoint : FeatureEntryPoint { data class Params( - val sessionVerificationRequestDetails: SessionVerificationRequestDetails, + val verificationRequest: VerificationRequest.Incoming, ) : NodeInputs fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt index 3bae35a3cf..3dfc7a2962 100644 --- a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt +++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt @@ -12,9 +12,13 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.matrix.api.verification.VerificationRequest interface VerifySessionEntryPoint : FeatureEntryPoint { - data class Params(val showDeviceVerifiedScreen: Boolean) : NodeInputs + data class Params( + val showDeviceVerifiedScreen: Boolean, + val verificationRequest: VerificationRequest.Outgoing, + ) : NodeInputs fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder @@ -25,8 +29,8 @@ interface VerifySessionEntryPoint : FeatureEntryPoint { } interface Callback : Plugin { - fun onEnterRecoveryKey() - fun onResetKey() + fun onLearnMoreAboutEncryption() + fun onBack() fun onDone() } } 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 f884f64510..802eadb13a 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 @@ -28,7 +28,7 @@ class IncomingVerificationNode @AssistedInject constructor( ) : Node(buildContext, plugins = plugins), IncomingVerificationNavigator { private val presenter = presenterFactory.create( - sessionVerificationRequestDetails = inputs().sessionVerificationRequestDetails, + verificationRequest = inputs().verificationRequest, navigator = this, ) 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 b3cec7def2..c241cef105 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 @@ -10,10 +10,12 @@ package io.element.android.features.verifysession.impl.incoming import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import com.freeletics.flowredux.compose.rememberStateAndDispatch import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -22,20 +24,25 @@ import io.element.android.features.verifysession.impl.incoming.IncomingVerificat import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.dateformatter.api.DateFormatter import io.element.android.libraries.dateformatter.api.DateFormatterMode +import io.element.android.libraries.di.annotations.SessionCoroutineScope 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 +import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch 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 class IncomingVerificationPresenter @AssistedInject constructor( - @Assisted private val sessionVerificationRequestDetails: SessionVerificationRequestDetails, + @Assisted private val verificationRequest: VerificationRequest.Incoming, @Assisted private val navigator: IncomingVerificationNavigator, + @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, private val sessionVerificationService: SessionVerificationService, private val stateMachine: IncomingVerificationStateMachine, private val dateFormatter: DateFormatter, @@ -43,49 +50,63 @@ class IncomingVerificationPresenter @AssistedInject constructor( @AssistedFactory interface Factory { fun create( - sessionVerificationRequestDetails: SessionVerificationRequestDetails, + verificationRequest: VerificationRequest.Incoming, navigator: IncomingVerificationNavigator, ): IncomingVerificationPresenter } @Composable override fun present(): IncomingVerificationState { - LaunchedEffect(Unit) { - // Force reset, just in case the service was left in a broken state - sessionVerificationService.reset( - cancelAnyPendingVerificationAttempt = false - ) - // Acknowledge the request right now - sessionVerificationService.acknowledgeVerificationRequest(sessionVerificationRequestDetails) - } + val coroutineScope = rememberCoroutineScope { Dispatchers.IO } + val stateAndDispatch = stateMachine.rememberStateAndDispatch() + + DisposableEffect(Unit) { + coroutineScope.launch { + // Force reset, just in case the service was left in a broken state + sessionVerificationService.reset( + cancelAnyPendingVerificationAttempt = false + ) + + // Start this after observing state machine + observeVerificationService() + + // Acknowledge the request right now + sessionVerificationService.acknowledgeVerificationRequest(verificationRequest) + } + + onDispose { + sessionCoroutineScope.launch { + val currentState = stateAndDispatch.state.value + sessionVerificationService.reset( + cancelAnyPendingVerificationAttempt = currentState?.isPending() == true, + ) + } + } + } + val formattedSignInTime = remember { dateFormatter.format( - timestamp = sessionVerificationRequestDetails.firstSeenTimestamp, + timestamp = verificationRequest.details.firstSeenTimestamp, mode = DateFormatterMode.TimeOrDate, ) } val step by remember { derivedStateOf { stateAndDispatch.state.value.toVerificationStep( - sessionVerificationRequestDetails = sessionVerificationRequestDetails, + sessionVerificationRequestDetails = verificationRequest.details, formattedSignInTime = formattedSignInTime, ) } } LaunchedEffect(stateAndDispatch.state.value) { - if ((stateAndDispatch.state.value as? IncomingVerificationStateMachine.State.Initial)?.isCancelled == true) { + if ((stateAndDispatch.state.value as? StateMachineState.Initial)?.isCancelled == true) { // The verification was canceled before it was started, maybe because another session accepted it navigator.onFinish() } } - // Start this after observing state machine - LaunchedEffect(Unit) { - observeVerificationService() - } - fun handleEvents(event: IncomingVerificationViewEvents) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { @@ -119,6 +140,7 @@ class IncomingVerificationPresenter @AssistedInject constructor( return IncomingVerificationState( step = step, + request = verificationRequest, eventSink = ::handleEvents, ) } @@ -129,36 +151,36 @@ class IncomingVerificationPresenter @AssistedInject constructor( ): Step = when (val machineState = this) { is StateMachineState.Initial, - IncomingVerificationStateMachine.State.AcceptingIncomingVerification, - IncomingVerificationStateMachine.State.RejectingIncomingVerification, + StateMachineState.AcceptingIncomingVerification, + StateMachineState.RejectingIncomingVerification, null -> { Step.Initial( - deviceDisplayName = sessionVerificationRequestDetails.displayName ?: sessionVerificationRequestDetails.deviceId.value, + deviceDisplayName = sessionVerificationRequestDetails.senderProfile.displayName ?: sessionVerificationRequestDetails.deviceId.value, deviceId = sessionVerificationRequestDetails.deviceId, formattedSignInTime = formattedSignInTime, - isWaiting = machineState == IncomingVerificationStateMachine.State.AcceptingIncomingVerification || - machineState == IncomingVerificationStateMachine.State.RejectingIncomingVerification, + isWaiting = machineState == StateMachineState.AcceptingIncomingVerification || + machineState == StateMachineState.RejectingIncomingVerification, ) } - is IncomingVerificationStateMachine.State.ChallengeReceived -> + is StateMachineState.ChallengeReceived -> Step.Verifying( data = machineState.data, isWaiting = false, ) - IncomingVerificationStateMachine.State.Completed -> Step.Completed - IncomingVerificationStateMachine.State.Canceling, - IncomingVerificationStateMachine.State.Failure -> Step.Failure - is IncomingVerificationStateMachine.State.AcceptingChallenge -> + StateMachineState.Completed -> Step.Completed + StateMachineState.Canceling, + StateMachineState.Failure -> Step.Failure + is StateMachineState.AcceptingChallenge -> Step.Verifying( data = machineState.data, isWaiting = true, ) - is IncomingVerificationStateMachine.State.RejectingChallenge -> + is StateMachineState.RejectingChallenge -> Step.Verifying( data = machineState.data, isWaiting = true, ) - IncomingVerificationStateMachine.State.Canceled -> Step.Canceled + StateMachineState.Canceled -> Step.Canceled } private fun CoroutineScope.observeVerificationService() { @@ -170,10 +192,10 @@ class IncomingVerificationPresenter @AssistedInject constructor( VerificationFlowState.DidAcceptVerificationRequest, VerificationFlowState.DidStartSasVerification -> Unit is VerificationFlowState.DidReceiveVerificationData -> { - stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) + stateMachine.dispatch(StateMachineEvent.DidReceiveChallenge(verificationAttemptState.data)) } VerificationFlowState.DidFinish -> { - stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidAcceptChallenge) + stateMachine.dispatch(StateMachineEvent.DidAcceptChallenge) } VerificationFlowState.DidCancel -> { // Can happen when: @@ -181,10 +203,10 @@ class IncomingVerificationPresenter @AssistedInject constructor( // - another session has accepted the incoming verification request // - the user reject the challenge from this application (I think this is an error). In this case, the state // machine will ignore this event and change state to Failure. - stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidCancel) + stateMachine.dispatch(StateMachineEvent.DidCancel) } VerificationFlowState.DidFail -> { - stateMachine.dispatch(IncomingVerificationStateMachine.Event.DidFail) + stateMachine.dispatch(StateMachineEvent.DidFail) } } } 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 241ea8e5ad..95f81af479 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 @@ -11,10 +11,12 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationRequest @Immutable data class IncomingVerificationState( val step: Step, + val request: VerificationRequest.Incoming, val eventSink: (IncomingVerificationViewEvents) -> Unit, ) { @Stable diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt index d873c6fd50..9ff9f50cd8 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/IncomingVerificationStateMachine.kt @@ -26,7 +26,7 @@ class IncomingVerificationStateMachine @Inject constructor( init { spec { inState { - on { _: Event.AcceptIncomingRequest, state -> + on { _, state -> state.override { State.AcceptingIncomingVerification.andLogStateChange() } } } @@ -39,23 +39,23 @@ class IncomingVerificationStateMachine @Inject constructor( } } inState { - on { _: Event.AcceptChallenge, state -> + on { _, state -> state.override { State.AcceptingChallenge(state.snapshot.data).andLogStateChange() } } - on { _: Event.DeclineChallenge, state -> + on { _, state -> state.override { State.RejectingChallenge(state.snapshot.data).andLogStateChange() } } } inState { - onEnterEffect { _ -> + onEnterEffect { sessionVerificationService.approveVerification() } - on { _: Event.DidAcceptChallenge, state -> + on { _, state -> state.override { State.Completed.andLogStateChange() } } } inState { - onEnterEffect { _ -> + onEnterEffect { sessionVerificationService.declineVerification() } } @@ -66,7 +66,7 @@ class IncomingVerificationStateMachine @Inject constructor( } inState { logReceivedEvents() - on { _: Event.Cancel, state: MachineState -> + on { _, state: MachineState -> when (state.snapshot) { State.Completed, State.Canceled -> state.noChange() else -> { @@ -75,7 +75,7 @@ class IncomingVerificationStateMachine @Inject constructor( } } } - on { _: Event.DidCancel, state: MachineState -> + on { _, state: MachineState -> when (state.snapshot) { is State.RejectingChallenge -> { state.override { State.Failure.andLogStateChange() } @@ -91,7 +91,7 @@ class IncomingVerificationStateMachine @Inject constructor( State.Failure -> state.noChange() } } - on { _: Event.DidFail, state: MachineState -> + on { _, state: MachineState -> state.override { State.Failure.andLogStateChange() } } } @@ -128,6 +128,11 @@ class IncomingVerificationStateMachine @Inject constructor( /** Verification failure. */ data object Failure : State + + fun isPending(): Boolean = when (this) { + AcceptingIncomingVerification, RejectingIncomingVerification, Failure, is ChallengeReceived, is AcceptingChallenge, is RejectingChallenge -> true + is Initial, Canceling, Canceled, Completed -> false + } } sealed interface Event { 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 d57f56385b..cdad2669d6 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 @@ -12,16 +12,32 @@ import io.element.android.features.verifysession.impl.incoming.IncomingVerificat import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData 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.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.verification.VerificationRequest open class IncomingVerificationStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anIncomingVerificationState(), - anIncomingVerificationState(step = aStepInitial(isWaiting = true)), + anIncomingVerificationState(step = aStepInitial(isWaiting = false), verificationRequest = anIncomingSessionVerificationRequest()), + anIncomingVerificationState(step = aStepInitial(isWaiting = false), verificationRequest = anIncomingUserVerificationRequest()), + anIncomingVerificationState(step = aStepInitial(isWaiting = true), verificationRequest = anIncomingSessionVerificationRequest()), + anIncomingVerificationState(step = aStepInitial(isWaiting = true), verificationRequest = anIncomingUserVerificationRequest()), anIncomingVerificationState(step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = false)), + anIncomingVerificationState( + step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = false), + verificationRequest = anIncomingUserVerificationRequest() + ), anIncomingVerificationState(step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = true)), + anIncomingVerificationState( + step = Step.Verifying(data = aEmojisSessionVerificationData(), isWaiting = true), + verificationRequest = anIncomingUserVerificationRequest() + ), anIncomingVerificationState(step = Step.Verifying(data = aDecimalsSessionVerificationData(), isWaiting = false)), anIncomingVerificationState(step = Step.Completed), + anIncomingVerificationState(step = Step.Completed, verificationRequest = anIncomingUserVerificationRequest()), anIncomingVerificationState(step = Step.Failure), anIncomingVerificationState(step = Step.Canceled), // Add other state here @@ -37,10 +53,38 @@ internal fun aStepInitial( isWaiting = isWaiting, ) +internal fun anIncomingSessionVerificationRequest() = VerificationRequest.Incoming.OtherSession( + details = SessionVerificationRequestDetails( + senderProfile = SessionVerificationRequestDetails.SenderProfile( + userId = UserId("@alice:example.com"), + displayName = "Alice", + avatarUrl = null, + ), + flowId = FlowId("1234"), + deviceId = DeviceId("ILAKNDNASDLK"), + firstSeenTimestamp = 0, + ) +) + +internal fun anIncomingUserVerificationRequest() = VerificationRequest.Incoming.User( + details = SessionVerificationRequestDetails( + senderProfile = SessionVerificationRequestDetails.SenderProfile( + userId = UserId("@alice:example.com"), + displayName = "Alice", + avatarUrl = null, + ), + flowId = FlowId("1234"), + deviceId = DeviceId("ILAKNDNASDLK"), + firstSeenTimestamp = 0, + ) +) + internal fun anIncomingVerificationState( step: Step = aStepInitial(), + verificationRequest: VerificationRequest.Incoming = anIncomingSessionVerificationRequest(), eventSink: (IncomingVerificationViewEvents) -> Unit = {}, ) = IncomingVerificationState( step = step, + request = verificationRequest, eventSink = eventSink, ) 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 b31c937f36..12bfe99fcd 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 @@ -13,9 +13,11 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBarDefaults.topAppBarColors 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.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter @@ -27,9 +29,11 @@ import io.element.android.features.verifysession.impl.incoming.IncomingVerificat import io.element.android.features.verifysession.impl.incoming.ui.SessionDetailsView import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying +import io.element.android.features.verifysession.impl.ui.VerificationUserProfileContent import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.PageTitle +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 @@ -38,6 +42,7 @@ 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.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.ui.strings.CommonStrings /** @@ -59,50 +64,80 @@ fun IncomingVerificationView( topBar = { TopAppBar( title = {}, + navigationIcon = { + when { + step is Step.Initial && !step.isWaiting -> Unit + step is Step.Completed -> Unit + else -> BackButton(onClick = { state.eventSink(IncomingVerificationViewEvents.GoBack) }) + } + }, + colors = topAppBarColors(containerColor = Color.Transparent), ) }, header = { - IncomingVerificationHeader(step = step) + IncomingVerificationHeader(step = step, request = state.request) }, footer = { IncomingVerificationBottomMenu( state = state, ) - } + }, + isScrollable = true, ) { IncomingVerificationContent( step = step, + request = state.request, ) } } @Composable -private fun IncomingVerificationHeader(step: Step) { +private fun IncomingVerificationHeader(step: Step, request: VerificationRequest.Incoming) { val iconStyle = when (step) { Step.Canceled -> BigIcon.Style.AlertSolid - is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) - is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) + is Step.Initial -> if (step.isWaiting) { + BigIcon.Style.Loading + } else { + when (request) { + is VerificationRequest.Incoming.OtherSession -> BigIcon.Style.Default(CompoundIcons.LockSolid()) + is VerificationRequest.Incoming.User -> BigIcon.Style.Default(CompoundIcons.UserProfileSolid()) + } + } + is Step.Verifying -> if (step.isWaiting) { + BigIcon.Style.Loading + } else { + BigIcon.Style.Default(CompoundIcons.ReactionSolid()) + } Step.Completed -> BigIcon.Style.SuccessSolid Step.Failure -> BigIcon.Style.AlertSolid } val titleTextId = when (step) { - Step.Canceled -> R.string.screen_session_verification_request_failure_title + Step.Canceled -> CommonStrings.common_verification_failed is Step.Initial -> R.string.screen_session_verification_request_title is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_title is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_title } - Step.Completed -> R.string.screen_session_verification_request_success_title + Step.Completed -> CommonStrings.common_verification_complete Step.Failure -> R.string.screen_session_verification_request_failure_title } val subtitleTextId = when (step) { Step.Canceled -> R.string.screen_session_verification_request_failure_subtitle - is Step.Initial -> R.string.screen_session_verification_request_subtitle + is Step.Initial -> when (request) { + is VerificationRequest.Incoming.OtherSession -> R.string.screen_session_verification_request_subtitle + is VerificationRequest.Incoming.User -> R.string.screen_session_verification_user_responder_subtitle + } is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle - is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_subtitle + is SessionVerificationData.Emojis -> when (request) { + is VerificationRequest.Incoming.OtherSession -> R.string.screen_session_verification_compare_emojis_subtitle + is VerificationRequest.Incoming.User -> R.string.screen_session_verification_compare_emojis_user_subtitle + } + } + Step.Completed -> when (request) { + is VerificationRequest.Incoming.OtherSession -> R.string.screen_session_verification_complete_subtitle + is VerificationRequest.Incoming.User -> R.string.screen_session_verification_complete_user_subtitle } - Step.Completed -> R.string.screen_session_verification_request_success_subtitle Step.Failure -> R.string.screen_session_verification_request_failure_subtitle } PageTitle( @@ -115,9 +150,10 @@ private fun IncomingVerificationHeader(step: Step) { @Composable private fun IncomingVerificationContent( step: Step, + request: VerificationRequest.Incoming, ) { when (step) { - is Step.Initial -> ContentInitial(step) + is Step.Initial -> ContentInitial(step, request) is Step.Verifying -> VerificationContentVerifying(step.data) else -> Unit } @@ -126,24 +162,40 @@ private fun IncomingVerificationContent( @Composable private fun ContentInitial( initialIncoming: Step.Initial, + request: VerificationRequest.Incoming, ) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(24.dp), - ) { - SessionDetailsView( - deviceName = initialIncoming.deviceDisplayName, - deviceId = initialIncoming.deviceId, - signInFormattedTimestamp = initialIncoming.formattedSignInTime, - ) - Text( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(bottom = 16.dp), - text = stringResource(R.string.screen_session_verification_request_footer), - style = ElementTheme.typography.fontBodyMdMedium, - textAlign = TextAlign.Center, - ) + when (request) { + is VerificationRequest.Incoming.OtherSession -> { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(24.dp), + ) { + SessionDetailsView( + deviceName = initialIncoming.deviceDisplayName, + deviceId = initialIncoming.deviceId, + signInFormattedTimestamp = initialIncoming.formattedSignInTime, + ) + Text( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = 16.dp), + text = stringResource(R.string.screen_session_verification_request_footer), + style = ElementTheme.typography.fontBodyMdMedium, + textAlign = TextAlign.Center, + ) + } + } + is VerificationRequest.Incoming.User -> { + Column( + modifier = Modifier.fillMaxWidth().padding(top = 24.dp), + ) { + VerificationUserProfileContent( + userId = request.details.senderProfile.userId, + displayName = request.details.senderProfile.displayName, + avatarUrl = request.details.senderProfile.avatarUrl, + ) + } + } } } @@ -157,16 +209,7 @@ private fun IncomingVerificationBottomMenu( when (step) { is Step.Initial -> { if (step.isWaiting) { - VerificationBottomMenu { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_identity_waiting_on_other_device), - onClick = {}, - enabled = false, - showProgress = true, - ) - InvisibleButton() - } + // Show nothing } else { VerificationBottomMenu { Button( @@ -184,14 +227,9 @@ private fun IncomingVerificationBottomMenu( } is Step.Verifying -> { if (step.isWaiting) { + // Add invisible buttons to keep the same screen layout VerificationBottomMenu { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_positive_button_verifying_ongoing), - onClick = {}, - enabled = false, - showProgress = true, - ) + InvisibleButton() InvisibleButton() } } else { diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt index dbd39c4a12..23236b32e0 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionNode.kt @@ -7,8 +7,6 @@ package io.element.android.features.verifysession.impl.outgoing -import android.app.Activity -import androidx.activity.compose.LocalActivity import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.bumble.appyx.core.modality.BuildContext @@ -18,10 +16,7 @@ import com.bumble.appyx.core.plugin.plugins import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode -import io.element.android.appconfig.LearnMoreConfig -import io.element.android.compound.theme.ElementTheme import io.element.android.features.verifysession.api.VerifySessionEntryPoint -import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -33,28 +28,22 @@ class VerifySelfSessionNode @AssistedInject constructor( ) : Node(buildContext, plugins = plugins) { private val callback = plugins().first() - private val presenter = presenterFactory.create( - showDeviceVerifiedScreen = inputs().showDeviceVerifiedScreen, - ) + private val inputs = inputs() - private fun onLearnMoreClick(activity: Activity, dark: Boolean) { - activity.openUrlInChromeCustomTab(null, dark, LearnMoreConfig.ENCRYPTION_URL) - } + private val presenter = presenterFactory.create( + showDeviceVerifiedScreen = inputs.showDeviceVerifiedScreen, + verificationRequest = inputs.verificationRequest, + ) @Composable override fun View(modifier: Modifier) { val state = presenter.present() - val activity = requireNotNull(LocalActivity.current) - val isDark = ElementTheme.isLightTheme.not() VerifySelfSessionView( state = state, modifier = modifier, - onLearnMoreClick = { - onLearnMoreClick(activity, isDark) - }, - onEnterRecoveryKey = callback::onEnterRecoveryKey, - onResetKey = callback::onResetKey, + onLearnMoreClick = callback::onLearnMoreAboutEncryption, onFinish = callback::onDone, + onBack = callback::onBack, ) } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt index c0760074ed..e7a05aa3e1 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenter.kt @@ -11,137 +11,113 @@ package io.element.android.features.verifysession.impl.outgoing import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import com.freeletics.flowredux.compose.rememberStateAndDispatch import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import io.element.android.features.logout.api.LogoutUseCase -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.runCatchingUpdatingState -import io.element.android.libraries.core.meta.BuildMeta 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.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationFlowState -import io.element.android.libraries.preferences.api.store.SessionPreferencesStore +import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import timber.log.Timber import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.Event as StateMachineEvent import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionStateMachine.State as StateMachineState class VerifySelfSessionPresenter @AssistedInject constructor( @Assisted private val showDeviceVerifiedScreen: Boolean, + @Assisted private val verificationRequest: VerificationRequest.Outgoing, private val sessionVerificationService: SessionVerificationService, private val encryptionService: EncryptionService, - private val stateMachine: VerifySelfSessionStateMachine, - private val buildMeta: BuildMeta, - private val sessionPreferencesStore: SessionPreferencesStore, - private val logoutUseCase: LogoutUseCase, ) : Presenter { @AssistedFactory interface Factory { - fun create(showDeviceVerifiedScreen: Boolean): VerifySelfSessionPresenter + fun create( + verificationRequest: VerificationRequest.Outgoing, + showDeviceVerifiedScreen: Boolean, + ): VerifySelfSessionPresenter } + private val stateMachine = VerifySelfSessionStateMachine( + sessionVerificationService = sessionVerificationService, + encryptionService = encryptionService, + ) + @Composable override fun present(): VerifySelfSessionState { - val coroutineScope = rememberCoroutineScope() - LaunchedEffect(Unit) { - // Force reset, just in case the service was left in a broken state - sessionVerificationService.reset(true) - } - val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() val stateAndDispatch = stateMachine.rememberStateAndDispatch() - val skipVerification by sessionPreferencesStore.isSessionVerificationSkipped().collectAsState(initial = false) + val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() - val signOutAction = remember { - mutableStateOf>(AsyncAction.Uninitialized) - } val step by remember { derivedStateOf { - if (skipVerification) { - VerifySelfSessionState.Step.Skipped - } else { - when (sessionVerifiedStatus) { - SessionVerifiedStatus.Unknown -> VerifySelfSessionState.Step.Loading - SessionVerifiedStatus.NotVerified -> { - stateAndDispatch.state.value.toVerificationStep( - canEnterRecoveryKey = recoveryState == RecoveryState.INCOMPLETE - ) - } - SessionVerifiedStatus.Verified -> { - if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) { - // The user has verified the session, we need to show the success screen - VerifySelfSessionState.Step.Completed - } else { - // Automatic verification, which can happen on freshly created account, in this case, skip the screen - VerifySelfSessionState.Step.Skipped + when (verificationRequest) { + is VerificationRequest.Outgoing.CurrentSession -> { + when (sessionVerifiedStatus) { + SessionVerifiedStatus.Unknown -> VerifySelfSessionState.Step.Loading + SessionVerifiedStatus.NotVerified -> { + stateAndDispatch.state.value.toVerificationStep() + } + SessionVerifiedStatus.Verified -> { + if (stateAndDispatch.state.value != StateMachineState.Initial || showDeviceVerifiedScreen) { + // The user has verified the session, we need to show the success screen + VerifySelfSessionState.Step.Completed + } else { + // Automatic verification, which can happen on freshly created account, in this case, skip the screen + VerifySelfSessionState.Step.Exit + } } } } + is VerificationRequest.Outgoing.User -> stateAndDispatch.state.value.toVerificationStep() } } } + // Start this after observing state machine LaunchedEffect(Unit) { + // Force reset, just in case the service was left in a broken state + sessionVerificationService.reset(cancelAnyPendingVerificationAttempt = true) + observeVerificationService() } fun handleEvents(event: VerifySelfSessionViewEvents) { Timber.d("Verification user action: ${event::class.simpleName}") when (event) { - VerifySelfSessionViewEvents.UseAnotherDevice -> stateAndDispatch.dispatchAction(StateMachineEvent.UseAnotherDevice) - VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification) + // Just relay the event to the state machine + VerifySelfSessionViewEvents.RequestVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.RequestVerification(verificationRequest)) VerifySelfSessionViewEvents.StartSasVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.StartSasVerification) VerifySelfSessionViewEvents.ConfirmVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.AcceptChallenge) VerifySelfSessionViewEvents.DeclineVerification -> stateAndDispatch.dispatchAction(StateMachineEvent.DeclineChallenge) VerifySelfSessionViewEvents.Cancel -> stateAndDispatch.dispatchAction(StateMachineEvent.Cancel) VerifySelfSessionViewEvents.Reset -> stateAndDispatch.dispatchAction(StateMachineEvent.Reset) - VerifySelfSessionViewEvents.SignOut -> coroutineScope.signOut(signOutAction) - VerifySelfSessionViewEvents.SkipVerification -> coroutineScope.launch { - sessionPreferencesStore.setSkipSessionVerification(true) - } } } return VerifySelfSessionState( step = step, - signOutAction = signOutAction.value, - displaySkipButton = buildMeta.isDebuggable, + request = verificationRequest, eventSink = ::handleEvents, ) } - private fun StateMachineState?.toVerificationStep( - canEnterRecoveryKey: Boolean - ): VerifySelfSessionState.Step = + private fun StateMachineState?.toVerificationStep(): VerifySelfSessionState.Step = when (val machineState = this) { StateMachineState.Initial, null -> { - VerifySelfSessionState.Step.Initial( - canEnterRecoveryKey = canEnterRecoveryKey, - isLastDevice = encryptionService.isLastDevice.value - ) + VerifySelfSessionState.Step.Initial } - VerifySelfSessionStateMachine.State.UseAnotherDevice -> { - VerifySelfSessionState.Step.UseAnotherDevice - } - StateMachineState.RequestingVerification, - StateMachineState.StartingSasVerification, - StateMachineState.SasVerificationStarted, - StateMachineState.Canceling -> { + is StateMachineState.RequestingVerification, + is StateMachineState.StartingSasVerification, + StateMachineState.SasVerificationStarted -> { VerifySelfSessionState.Step.AwaitingOtherDeviceResponse } @@ -149,7 +125,7 @@ class VerifySelfSessionPresenter @AssistedInject constructor( VerifySelfSessionState.Step.Ready } - StateMachineState.Canceled -> { + is StateMachineState.Canceled -> { VerifySelfSessionState.Step.Canceled } @@ -164,6 +140,10 @@ class VerifySelfSessionPresenter @AssistedInject constructor( StateMachineState.Completed -> { VerifySelfSessionState.Step.Completed } + + StateMachineState.Exit -> { + VerifySelfSessionState.Step.Exit + } } private fun CoroutineScope.observeVerificationService() { @@ -171,33 +151,27 @@ class VerifySelfSessionPresenter @AssistedInject constructor( .onEach { Timber.d("Verification flow state: ${it::class.simpleName}") } .onEach { verificationAttemptState -> when (verificationAttemptState) { - VerificationFlowState.Initial -> stateMachine.dispatch(VerifySelfSessionStateMachine.Event.Reset) + VerificationFlowState.Initial -> stateMachine.dispatch(StateMachineEvent.Reset) VerificationFlowState.DidAcceptVerificationRequest -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptVerificationRequest) + stateMachine.dispatch(StateMachineEvent.DidAcceptVerificationRequest) } VerificationFlowState.DidStartSasVerification -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidStartSasVerification) + stateMachine.dispatch(StateMachineEvent.DidStartSasVerification) } is VerificationFlowState.DidReceiveVerificationData -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidReceiveChallenge(verificationAttemptState.data)) + stateMachine.dispatch(StateMachineEvent.DidReceiveChallenge(verificationAttemptState.data)) } VerificationFlowState.DidFinish -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidAcceptChallenge) + stateMachine.dispatch(StateMachineEvent.DidAcceptChallenge) } VerificationFlowState.DidCancel -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidCancel) + stateMachine.dispatch(StateMachineEvent.DidCancel) } VerificationFlowState.DidFail -> { - stateMachine.dispatch(VerifySelfSessionStateMachine.Event.DidFail) + stateMachine.dispatch(StateMachineEvent.DidFail) } } } .launchIn(this) } - - private fun CoroutineScope.signOut(signOutAction: MutableState>) = launch { - suspend { - logoutUseCase.logout(ignoreSdkError = true) - }.runCatchingUpdatingState(signOutAction) - } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt index 3c998247c7..a8f5a6771b 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionState.kt @@ -9,29 +9,25 @@ package io.element.android.features.verifysession.impl.outgoing import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable -import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.api.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationRequest @Immutable data class VerifySelfSessionState( val step: Step, - val signOutAction: AsyncAction, - val displaySkipButton: Boolean, + val request: VerificationRequest.Outgoing, val eventSink: (VerifySelfSessionViewEvents) -> Unit, ) { @Stable sealed interface Step { data object Loading : Step - - // FIXME canEnterRecoveryKey value is never read. - data class Initial(val canEnterRecoveryKey: Boolean, val isLastDevice: Boolean = false) : Step - data object UseAnotherDevice : Step + data object Initial : Step data object Canceled : Step data object AwaitingOtherDeviceResponse : Step data object Ready : Step data class Verifying(val data: SessionVerificationData, val state: AsyncData) : Step data object Completed : Step - data object Skipped : Step + data object Exit : Step } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt index a71b09c783..746cbe8aa4 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateMachine.kt @@ -19,39 +19,37 @@ 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.verification.SessionVerificationData import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.VerificationRequest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.timeout -import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import com.freeletics.flowredux.dsl.State as MachineState @OptIn(FlowPreview::class) -class VerifySelfSessionStateMachine @Inject constructor( +class VerifySelfSessionStateMachine( private val sessionVerificationService: SessionVerificationService, private val encryptionService: EncryptionService, ) : FlowReduxStateMachine( - initialState = State.Initial + initialState = State.Initial, ) { init { spec { inState { - on { _: Event.UseAnotherDevice, state -> - state.override { State.UseAnotherDevice.andLogStateChange() } - } - } - inState { - on { _: Event.RequestVerification, state -> - state.override { State.RequestingVerification.andLogStateChange() } + on { event, state -> + state.override { State.RequestingVerification(event.verificationRequest).andLogStateChange() } } } inState { - onEnterEffect { - sessionVerificationService.requestVerification() + onEnterEffect { event -> + when (event.verificationRequest) { + is VerificationRequest.Outgoing.CurrentSession -> sessionVerificationService.requestCurrentSessionVerification() + is VerificationRequest.Outgoing.User -> sessionVerificationService.requestUserVerification(event.verificationRequest.userId) + } } - on { _: Event.DidAcceptVerificationRequest, state -> + on { _, state -> state.override { State.VerificationRequestAccepted.andLogStateChange() } } } @@ -61,25 +59,26 @@ class VerifySelfSessionStateMachine @Inject constructor( } } inState { - on { _: Event.StartSasVerification, state -> + on { _, state -> state.override { State.StartingSasVerification.andLogStateChange() } } } inState { - on { _: Event.Reset, state -> + on { _, state -> + sessionVerificationService.reset(cancelAnyPendingVerificationAttempt = false) state.override { State.Initial.andLogStateChange() } } } inState { - on { event: Event.DidReceiveChallenge, state -> + on { event, state -> state.override { State.Verifying.ChallengeReceived(event.data).andLogStateChange() } } } inState { - on { _: Event.AcceptChallenge, state -> + on { _, state -> state.override { State.Verifying.Replying(state.snapshot.data, accept = true).andLogStateChange() } } - on { _: Event.DeclineChallenge, state -> + on { _, state -> state.override { State.Verifying.Replying(state.snapshot.data, accept = false).andLogStateChange() } } } @@ -91,7 +90,7 @@ class VerifySelfSessionStateMachine @Inject constructor( sessionVerificationService.declineVerification() } } - on { _: Event.DidAcceptChallenge, state -> + on { _, state -> // If a key backup exists, wait until it's restored or a timeout happens val hasBackup = encryptionService.doesBackupExistOnServer().getOrNull().orFalse() if (hasBackup) { @@ -104,21 +103,14 @@ class VerifySelfSessionStateMachine @Inject constructor( state.override { State.Completed.andLogStateChange() } } } - inState { - // TODO The 'Canceling' -> 'Canceled' transitions doesn't seem to work anymore, check if something changed in the Rust SDK - onEnterEffect { - sessionVerificationService.cancelVerification() - } - } inState { logReceivedEvents() - on { _: Event.DidStartSasVerification, state: MachineState -> + on { _, state: MachineState -> state.override { State.SasVerificationStarted.andLogStateChange() } } - on { _: Event.Cancel, state: MachineState -> + on { event, state: MachineState -> when (state.snapshot) { - State.Initial, State.Completed, State.Canceled -> state.noChange() - State.UseAnotherDevice -> state.override { State.Initial.andLogStateChange() } + State.Initial, State.Completed, is State.Canceled -> state.override { State.Exit } // For some reason `cancelVerification` is not calling its delegate `didCancel` method so we don't pass from // `Canceling` state to `Canceled` automatically anymore else -> { @@ -127,28 +119,22 @@ class VerifySelfSessionStateMachine @Inject constructor( } } } - on { _: Event.DidCancel, state: MachineState -> + on { event, state: MachineState -> state.override { State.Canceled.andLogStateChange() } } - on { _: Event.DidFail, state: MachineState -> - when (state.snapshot) { - is State.RequestingVerification -> state.override { State.Initial.andLogStateChange() } - else -> state.override { State.Canceled.andLogStateChange() } - } + on { event, state: MachineState -> + state.override { State.Canceled.andLogStateChange() } } } } } sealed interface State { - /** The initial state, before verification started. */ + /** Let the user know that they need to get ready on their other session. */ data object Initial : State - /** Let the user know that they need to get ready on their other session. */ - data object UseAnotherDevice : State - /** Waiting for verification acceptance. */ - data object RequestingVerification : State + data class RequestingVerification(val verificationRequest: VerificationRequest.Outgoing) : State /** Verification request accepted. Waiting for start. */ data object VerificationRequestAccepted : State @@ -167,22 +153,18 @@ class VerifySelfSessionStateMachine @Inject constructor( data class Replying(override val data: SessionVerificationData, val accept: Boolean) : Verifying(data) } - /** The verification is being canceled. */ - data object Canceling : State - /** The verification has been canceled, remotely or locally. */ data object Canceled : State /** Verification successful. */ data object Completed : State + + data object Exit : State } sealed interface Event { - /** User wants to use another session. */ - data object UseAnotherDevice : Event - /** Request verification. */ - data object RequestVerification : Event + data class RequestVerification(val verificationRequest: VerificationRequest.Outgoing) : Event /** The current verification request has been accepted. */ data object DidAcceptVerificationRequest : Event diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt index 9ebb80b68b..ec3e1c29e2 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionStateProvider.kt @@ -11,18 +11,36 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.features.verifysession.impl.ui.aDecimalsSessionVerificationData import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificationData -import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.verification.VerificationRequest open class VerifySelfSessionStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aVerifySelfSessionState(displaySkipButton = true), aVerifySelfSessionState( - step = Step.AwaitingOtherDeviceResponse + step = Step.Initial, + request = anOutgoingSessionVerificationRequest(), ), aVerifySelfSessionState( - step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized) + step = Step.Initial, + request = anOutgoingUserVerificationRequest(), + ), + aVerifySelfSessionState( + step = Step.AwaitingOtherDeviceResponse, + request = anOutgoingSessionVerificationRequest(), + ), + aVerifySelfSessionState( + step = Step.AwaitingOtherDeviceResponse, + request = anOutgoingUserVerificationRequest(), + ), + aVerifySelfSessionState( + step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized), + request = anOutgoingSessionVerificationRequest(), + ), + aVerifySelfSessionState( + step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Uninitialized), + request = anOutgoingUserVerificationRequest(), ), aVerifySelfSessionState( step = Step.Verifying(aEmojisSessionVerificationData(), AsyncData.Loading()) @@ -37,40 +55,32 @@ open class VerifySelfSessionStateProvider : PreviewParameterProvider = AsyncAction.Uninitialized, - displaySkipButton: Boolean = false, + step: Step = Step.Initial, + request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), eventSink: (VerifySelfSessionViewEvents) -> Unit = {}, ) = VerifySelfSessionState( step = step, - displaySkipButton = displaySkipButton, + request = request, eventSink = eventSink, - signOutAction = signOutAction, ) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt index 2a73a4b627..6a00daa058 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionView.kt @@ -16,13 +16,11 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.TopAppBarDefaults.topAppBarColors 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.platform.LocalInspectionMode +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -32,22 +30,21 @@ import io.element.android.features.verifysession.impl.R import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.features.verifysession.impl.ui.VerificationBottomMenu import io.element.android.features.verifysession.impl.ui.VerificationContentVerifying -import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.PageTitle -import io.element.android.libraries.designsystem.components.ProgressDialog +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.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.InvisibleButton -import io.element.android.libraries.designsystem.theme.components.OutlinedButton 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.verification.SessionVerificationData +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -55,18 +52,16 @@ import io.element.android.libraries.ui.strings.CommonStrings fun VerifySelfSessionView( state: VerifySelfSessionState, onLearnMoreClick: () -> Unit, - onEnterRecoveryKey: () -> Unit, - onResetKey: () -> Unit, onFinish: () -> Unit, + onBack: () -> Unit, modifier: Modifier = Modifier, ) { val step = state.step fun cancelOrResetFlow() { when (step) { is Step.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset) - is Step.AwaitingOtherDeviceResponse, - Step.UseAnotherDevice, - Step.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel) + Step.Initial, Step.Completed -> onBack() + Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(VerifySelfSessionViewEvents.Cancel) is Step.Verifying -> { if (!step.state.isLoading()) { state.eventSink(VerifySelfSessionViewEvents.DeclineVerification) @@ -76,18 +71,11 @@ fun VerifySelfSessionView( } } - val latestOnFinish by rememberUpdatedState(newValue = onFinish) - LaunchedEffect(step, latestOnFinish) { - if (step is Step.Skipped) { - latestOnFinish() - } - } BackHandler { cancelOrResetFlow() } - if (step is Step.Loading || - step is Step.Skipped) { + if (step is Step.Loading) { // Just display a loader in this case, to avoid UI glitch. Box( modifier = Modifier.fillMaxSize(), @@ -101,94 +89,96 @@ fun VerifySelfSessionView( topBar = { TopAppBar( title = {}, - actions = { - if (step !is Step.Completed && - state.displaySkipButton && - LocalInspectionMode.current.not()) { - TextButton( - text = stringResource(CommonStrings.action_skip), - onClick = { state.eventSink(VerifySelfSessionViewEvents.SkipVerification) } - ) - } - if (step is Step.Initial) { - TextButton( - text = stringResource(CommonStrings.action_signout), - onClick = { state.eventSink(VerifySelfSessionViewEvents.SignOut) } - ) - } - } + navigationIcon = if (step != Step.Completed) { + { BackButton(onClick = ::cancelOrResetFlow) } + } else { + {} + }, + colors = topAppBarColors(containerColor = Color.Transparent) ) }, header = { - VerifySelfSessionHeader(step = step) + VerifySelfSessionHeader(step = step, request = state.request) }, footer = { VerifySelfSessionBottomMenu( screenState = state, onCancelClick = ::cancelOrResetFlow, - onEnterRecoveryKey = onEnterRecoveryKey, onContinueClick = onFinish, - onResetKey = onResetKey, ) - } + }, + isScrollable = true, ) { VerifySelfSessionContent( flowState = step, + request = state.request, onLearnMoreClick = onLearnMoreClick, ) } } - - when (state.signOutAction) { - AsyncAction.Loading -> { - ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content)) - } - is AsyncAction.Success, - is AsyncAction.Confirming, - is AsyncAction.Failure, - AsyncAction.Uninitialized -> Unit - } } @Composable -private fun VerifySelfSessionHeader(step: Step) { +private fun VerifySelfSessionHeader(step: Step, request: VerificationRequest.Outgoing) { val iconStyle = when (step) { Step.Loading -> error("Should not happen") - is Step.Initial -> BigIcon.Style.Default(CompoundIcons.LockSolid()) - Step.UseAnotherDevice -> BigIcon.Style.Default(CompoundIcons.Devices()) - Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Default(CompoundIcons.Devices()) + Step.Initial -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> BigIcon.Style.Default(CompoundIcons.Devices()) + is VerificationRequest.Outgoing.User -> BigIcon.Style.Default(CompoundIcons.LockSolid()) + } + Step.AwaitingOtherDeviceResponse -> BigIcon.Style.Loading Step.Canceled -> BigIcon.Style.AlertSolid - Step.Ready, is Step.Verifying -> BigIcon.Style.Default(CompoundIcons.Reaction()) + Step.Ready -> BigIcon.Style.Default(CompoundIcons.ReactionSolid()) Step.Completed -> BigIcon.Style.SuccessSolid - is Step.Skipped -> return + is Step.Verifying -> { + if (step.state is AsyncData.Loading) { + BigIcon.Style.Loading + } else { + BigIcon.Style.Default(CompoundIcons.ReactionSolid()) + } + } + is Step.Exit -> return } val titleTextId = when (step) { Step.Loading -> error("Should not happen") - is Step.Initial -> R.string.screen_identity_confirmation_title - Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_title - Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_title + Step.Initial -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_session_verification_use_another_device_title + is VerificationRequest.Outgoing.User -> R.string.screen_session_verification_user_initiator_title + } + Step.AwaitingOtherDeviceResponse -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_session_verification_waiting_another_device_title + is VerificationRequest.Outgoing.User -> R.string.screen_session_verification_waiting_other_user_title + } Step.Canceled -> CommonStrings.common_verification_failed Step.Ready -> R.string.screen_session_verification_compare_emojis_title - Step.Completed -> R.string.screen_identity_confirmed_title + Step.Completed -> 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 } - is Step.Skipped -> return + is Step.Exit -> return } val subtitleTextId = when (step) { Step.Loading -> error("Should not happen") - is Step.Initial -> R.string.screen_identity_confirmation_subtitle - Step.UseAnotherDevice -> R.string.screen_session_verification_use_another_device_subtitle - Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_another_device_subtitle + Step.Initial -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_session_verification_use_another_device_subtitle + is VerificationRequest.Outgoing.User -> R.string.screen_session_verification_user_initiator_subtitle + } + Step.AwaitingOtherDeviceResponse -> R.string.screen_session_verification_waiting_subtitle Step.Canceled -> R.string.screen_session_verification_failed_subtitle Step.Ready -> R.string.screen_session_verification_ready_subtitle - Step.Completed -> R.string.screen_identity_confirmed_subtitle + Step.Completed -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_identity_confirmed_subtitle + is VerificationRequest.Outgoing.User -> R.string.screen_session_verification_complete_user_subtitle + } is Step.Verifying -> when (step.data) { is SessionVerificationData.Decimals -> R.string.screen_session_verification_compare_numbers_subtitle - is SessionVerificationData.Emojis -> R.string.screen_session_verification_compare_emojis_subtitle + is SessionVerificationData.Emojis -> when (request) { + is VerificationRequest.Outgoing.CurrentSession -> R.string.screen_session_verification_compare_emojis_subtitle + is VerificationRequest.Outgoing.User -> R.string.screen_session_verification_compare_emojis_user_subtitle + } } - is Step.Skipped -> return + is Step.Exit -> return } PageTitle( @@ -201,11 +191,15 @@ private fun VerifySelfSessionHeader(step: Step) { @Composable private fun VerifySelfSessionContent( flowState: Step, + request: VerificationRequest.Outgoing, onLearnMoreClick: () -> Unit, ) { when (flowState) { is Step.Initial -> { - ContentInitial(onLearnMoreClick) + when (request) { + is VerificationRequest.Outgoing.CurrentSession -> Unit + is VerificationRequest.Outgoing.User -> ContentInitial(onLearnMoreClick) + } } is Step.Verifying -> { VerificationContentVerifying(flowState.data) @@ -235,8 +229,6 @@ private fun ContentInitial( @Composable private fun VerifySelfSessionBottomMenu( screenState: VerifySelfSessionState, - onEnterRecoveryKey: () -> Unit, - onResetKey: () -> Unit, onCancelClick: () -> Unit, onContinueClick: () -> Unit, ) { @@ -248,27 +240,6 @@ private fun VerifySelfSessionBottomMenu( when (verificationViewState) { Step.Loading -> error("Should not happen") is Step.Initial -> { - VerificationBottomMenu { - if (verificationViewState.isLastDevice.not()) { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_identity_use_another_device), - onClick = { eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) }, - ) - } - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_session_verification_enter_recovery_key), - onClick = onEnterRecoveryKey, - ) - OutlinedButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_identity_confirmation_cannot_confirm), - onClick = onResetKey, - ) - } - } - is Step.UseAnotherDevice -> { VerificationBottomMenu { Button( modifier = Modifier.fillMaxWidth(), @@ -302,39 +273,24 @@ private fun VerifySelfSessionBottomMenu( ) } } - is Step.AwaitingOtherDeviceResponse -> { - VerificationBottomMenu { - Button( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.screen_identity_waiting_on_other_device), - onClick = {}, - showProgress = true, - enabled = false, - ) - InvisibleButton() - } - } + is Step.AwaitingOtherDeviceResponse -> Unit is Step.Verifying -> { - val positiveButtonTitle = if (isVerifying) { - stringResource(R.string.screen_session_verification_positive_button_verifying_ongoing) - } else { - stringResource(R.string.screen_session_verification_they_match) - } - VerificationBottomMenu { - Button( - modifier = Modifier.fillMaxWidth(), - text = positiveButtonTitle, - showProgress = isVerifying, - enabled = !isVerifying, - onClick = { - if (!isVerifying) { - eventSink(VerifySelfSessionViewEvents.ConfirmVerification) - } - }, - ) - if (isVerifying) { + if (isVerifying) { + // Add invisible buttons to keep the same screen layout + VerificationBottomMenu { InvisibleButton() - } else { + InvisibleButton() + } + } else { + VerificationBottomMenu { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(R.string.screen_session_verification_they_match), + onClick = { + eventSink(VerifySelfSessionViewEvents.ConfirmVerification) + }, + ) + TextButton( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.screen_session_verification_they_dont_match), @@ -353,7 +309,7 @@ private fun VerifySelfSessionBottomMenu( InvisibleButton() } } - is Step.Skipped -> return + is Step.Exit -> return } } @@ -363,8 +319,7 @@ internal fun VerifySelfSessionViewPreview(@PreviewParameter(VerifySelfSessionSta VerifySelfSessionView( state = state, onLearnMoreClick = {}, - onEnterRecoveryKey = {}, - onResetKey = {}, onFinish = {}, + onBack = {}, ) } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt index 752dbc3d7a..281991cb2d 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewEvents.kt @@ -8,13 +8,10 @@ package io.element.android.features.verifysession.impl.outgoing sealed interface VerifySelfSessionViewEvents { - data object UseAnotherDevice : VerifySelfSessionViewEvents data object RequestVerification : VerifySelfSessionViewEvents data object StartSasVerification : VerifySelfSessionViewEvents data object ConfirmVerification : VerifySelfSessionViewEvents data object DeclineVerification : VerifySelfSessionViewEvents data object Cancel : VerifySelfSessionViewEvents data object Reset : VerifySelfSessionViewEvents - data object SignOut : VerifySelfSessionViewEvents - data object SkipVerification : VerifySelfSessionViewEvents } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt index a3724912a1..8859a4180b 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/Common.kt @@ -23,11 +23,11 @@ internal fun aDecimalsSessionVerificationData( } private fun aVerificationEmojiList() = listOf( - VerificationEmoji(number = 27, emoji = "🍕", description = "Pizza"), - VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), - VerificationEmoji(number = 54, emoji = "🚀", description = "Rocket"), - VerificationEmoji(number = 42, emoji = "📕", description = "Book"), - VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), - VerificationEmoji(number = 48, emoji = "🔨", description = "Hammer"), - VerificationEmoji(number = 63, emoji = "📌", description = "Pin"), + VerificationEmoji(number = 27), + VerificationEmoji(number = 54), + VerificationEmoji(number = 54), + VerificationEmoji(number = 42), + VerificationEmoji(number = 48), + VerificationEmoji(number = 48), + VerificationEmoji(number = 63), ) diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt index 12775cd2e0..b2010bd61c 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationContentVerifying.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize 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.widthIn import androidx.compose.runtime.Composable @@ -38,7 +39,7 @@ internal fun VerificationContentVerifying( modifier: Modifier = Modifier, ) { Box( - modifier = modifier.fillMaxSize(), + modifier = modifier.fillMaxSize().padding(bottom = 20.dp), contentAlignment = Alignment.Center ) { when (data) { @@ -86,8 +87,8 @@ private fun EmojiItemView(emoji: VerificationEmoji, modifier: Modifier = Modifie text = stringResource(id = emojiResource.nameRes), style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, + overflow = TextOverflow.Visible, + textAlign = TextAlign.Center, ) } } 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 new file mode 100644 index 0000000000..3b10499b39 --- /dev/null +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/ui/VerificationUserProfileContent.kt @@ -0,0 +1,73 @@ +/* + * 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.verifysession.impl.ui + +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.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.UserId + +@Composable +fun VerificationUserProfileContent( + userId: UserId, + displayName: String?, + avatarUrl: String?, + modifier: Modifier = Modifier, +) { + val avatarData = remember(userId, displayName, avatarUrl) { + AvatarData(id = userId.value, name = displayName, url = avatarUrl, size = AvatarSize.UserVerification) + } + + Row( + modifier = modifier.fillMaxWidth() + .clip(RoundedCornerShape(8.dp)) + .background(ElementTheme.colors.bgSubtleSecondary) + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Avatar(avatarData) + + Spacer(modifier = Modifier.padding(12.dp)) + + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + Text(text = displayName ?: userId.value, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary) + + if (displayName != null) { + Text(text = userId.value, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textSecondary) + } + } + } +} + +@PreviewsDayNight +@Composable +internal fun VerificationUserProfileContentPreview() = ElementPreview { + VerificationUserProfileContent( + userId = UserId("@alice:example.com"), + displayName = "Alice", + avatarUrl = "https://example.com/avatar.png", + ) +} 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 9dd9ec38b5..e22eb179b0 100644 --- a/features/verifysession/impl/src/main/res/values-es/translations.xml +++ b/features/verifysession/impl/src/main/res/values-es/translations.xml @@ -1,28 +1,52 @@ + "¿No puedes confirmar?" + "Crear una nueva clave de recuperación" "Verifica este dispositivo para configurar la mensajería segura." "Confirma que eres tú" "Usar otro dispositivo" + "Usar clave de recuperación" "Ahora puedes leer o enviar mensajes de forma segura y cualquier persona con la que chatees también puede confiar en este dispositivo." "Dispositivo verificado" "Usar otro dispositivo" + "Esperando en otro dispositivo…" "Algo no fue bien. Se agotó el tiempo de espera de la solicitud o se rechazó." "Confirma que los emojis que aparecen a continuación coinciden con los que aparecen en tu otra sesión." "Comparar emojis" + "Confirma que los emojis que aparecen a continuación coinciden con los mostrados en el dispositivo del otro usuario." "Confirma que los números que aparecen a continuación coinciden con los mostrados en tu otra sesión." "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." "Introduzca 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." "Abrir una sesión existente" "Reintentar la verificación" "Estoy listo" - "Esperando a que coincida" + "A la espera de que coincida…" "Compara un conjunto único de emojis." "Compara los emoji, asegurándote de que aparecen en el mismo orden." + "Sesión iniciada" + "O bien se agotó el tiempo de solicitud, se rechazó la solicitud o hubo una discrepancia en la verificación." + "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." "Dispositivo verificado" + "Verificación solicitada" "No coinciden" "Coinciden" + "Asegúrate de tener la aplicación abierta en el otro dispositivo antes de iniciar la verificación desde aquí." + "Abre la aplicación en otro dispositivo verificado" + "Para mayor seguridad, verifica a este usuario comparando un conjunto de emojis en vuestros dispositivos. Hazlo utilizando una forma de comunicación de confianza." + "¿Verificar a este usuario?" + "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" + "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." "A la espera de aceptar la solicitud" "Cerrando sesión…" diff --git a/features/verifysession/impl/src/main/res/values-eu/translations.xml b/features/verifysession/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..999e7ca75d --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,32 @@ + + + "Ezin duzu baieztatu?" + "Sortu berreskuratze-gako berria" + "Egiaztatu gailua mezularitza segurua konfiguratzeko." + "Berretsi zure identitatea" + "Erabili beste gailu bat" + "Erabili berreskuratze-gakoa" + "Orain mezuak modu seguruan irakurri edo bidal ditzakezu, eta txateatzen duzun edonor ere fida daiteke gailu honetaz." + "Gailua egiaztatu da" + "Erabili beste gailu bat" + "Beste gailuaren zain…" + "Egiaztatu ondorengo emojiak bat datozela beste saioan erakusten direnekin." + "Alderatu emojiak" + "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." + "Sartu berreskuratze-gakoa" + "Frogatu zeu zarela zifratutako mezuen historia atzitzeko." + "Ireki lehendik hasita dagoen saio bat" + "Saiatu berriro egiaztatzen" + "Prest nago" + "Bat etorriko zain…" + "Saioa hasita" + "Egiaztapenak huts egin du" + "Gailua egiaztatu da" + "Ez datoz bat" + "Bat datoz" + "Jarraitzeko, onartu zure beste saioan egiaztapen-prozesua hasteko eskaera." + "Eskaera onartzeko zain" + "Saioa amaitzen…" + 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 c011022eb7..49e09aab99 100644 --- a/features/verifysession/impl/src/main/res/values-fi/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fi/translations.xml @@ -11,11 +11,13 @@ "Käytä toista laitetta" "Odotetaan toista laitetta…" "Jokin ei vaikuta oikealta. Joko pyyntö aikakatkaistiin tai hylättiin." - "Vahvista, että alla olevat hymiöt vastaavat toisella laitteella näkyviä hymiöitä." - "Vertaa hymiöitä" + "Vahvista, että alla olevat emojit vastaavat toisella laitteella näkyviä emojeja." + "Vertaa emojeja" + "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 luottaa tämän käyttäjän identiteettiin, kun lähetät tai vastaanotat viestejä." "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." @@ -24,7 +26,7 @@ "Olen valmis" "Odotetaan vahvistusta…" "Vertaa emojisarjaa." - "Vertaa hymiöitä, varmistaen että ne ovat samassa järjestyksessä." + "Vertaa emojeja, varmistaen että ne ovat samassa järjestyksessä." "Kirjautui sisään" "Joko pyyntö aikakatkaistiin, pyyntö hylättiin tai vahvistus ei täsmännyt." "Vahvistus epäonnistui" @@ -37,8 +39,14 @@ "Ne täsmäävät" "Varmista, että sovellus on avoinna toisessa laitteessa, ennen kuin aloitat vahvistuksen tästä." "Avaa sovellus toisella vahvistetulla laitteella" + "Vahvista tämä käyttäjä turvallisuuden lisäämiseksi vertaamalla emojeja laitteillanne. Tee tämä käyttämällä luotettavaa viestintätapaa." + "Vahvistetaanko tämä käyttäjä?" + "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" + "Odotetaan toista käyttäjää" + "Kun pyyntö on hyväksytty, voit jatkaa vahvistusta." "Hyväksy vahvistuspyyntö toisella laitteella jatkaaksesi." "Odotetaan pyynnön hyväksymistä" "Kirjaudutaan ulos…" 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 2ec52d94fc..a675c879f2 100644 --- a/features/verifysession/impl/src/main/res/values-fr/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -11,24 +11,24 @@ "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 emojis ci-dessous correspondent à ceux affichés sur votre autre session." + "Confirmez que les émojis ci-dessous correspondent à ceux affichés sur votre autre session." "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 être sûr(e) de l’identité de cet utilisateur lors de l’envoi ou de la réception de messages." + "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." "Utiliser la clé de récupération" - "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification." + "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." "Ouvrir une session existante" "Réessayer la vérification" "Je suis prêt.e" "En attente de correspondance…" - "Comparer un groupe unique d’Emojis." - "Comparez les emoji uniques en veillant à ce qu’ils apparaissent dans le même ordre." + "Comparer un groupe unique d’émojis." + "Comparez les émojis uniques en veillant à ce qu’ils apparaissent dans le même ordre." "Connecté" - "Soit la demande a expiré, soit elle a été refusée, soit il y a eu une non-concordance de vérification." + "Soit la demande a expiré, soit elle a été refusée, soit les éléments à comparer ne correspondaient pas." "Échec de la vérification" "Continuez uniquement si c’est vous qui avez commencé cette vérification." "Vérifiez l’autre appareil pour sécuriser l’historique de vos messages." @@ -41,12 +41,12 @@ "Ouvrez l’application sur un autre appareil vérifié" "Pour plus de sécurité, vérifiez cet utilisateur en comparant des émojis sur vos appareils. Pour ce faire, utilisez un moyen de communication fiable." "Vérifier cet utilisateur ?" - "Pour plus de sécurité, un autre utilisateur souhaite vérifier votre identité. Des émojis à comparer vous seront présentés." + "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" "En attente de l’autre utilisateur" - "Une fois accepté, vous pourrez poursuivre la vérification." + "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." "En attente d’acceptation de la demande" "Déconnexion…" 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 322ab0e6d3..bb55310d31 100644 --- a/features/verifysession/impl/src/main/res/values-it/translations.xml +++ b/features/verifysession/impl/src/main/res/values-it/translations.xml @@ -13,9 +13,11 @@ "C\'è qualcosa che non va. La richiesta è scaduta o è stata rifiutata." "Verifica che gli emoji sottostanti corrispondano a quelli mostrati nell\'altra sessione." "Confronta le emoji" + "Conferma che le emoji qui sotto corrispondano a quelle visualizzate sul dispositivo dell\'altro utente." "Conferma che i numeri seguenti corrispondano a quelli mostrati nell\'altra sessione." "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." "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." @@ -37,8 +39,14 @@ "Corrispondono" "Assicurati di avere l\'app aperta sull\'altro dispositivo prima di iniziare la verifica da qui." "Apri l\'app su un altro dispositivo verificato" + "Per una maggiore sicurezza, verifica questo utente confrontando un set di emoji sui tuoi dispositivi. A tale scopo, utilizza un metodo di comunicazione affidabile." + "Verificare questo utente?" + "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" + "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." "In attesa di accettare la richiesta" "Disconnessione in corso…" 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 89f1a068ea..5815ecefaa 100644 --- a/features/verifysession/impl/src/main/res/values-ka/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ka/translations.xml @@ -1,11 +1,18 @@ + "ახალი აღდგენის გასაღების შექმნა" + "დაადასტურეთ ეს მოწყობილობა უსაფრთხო მიმოწერისათვის." + "დაამტკიცეთ თქვენი პიროვნება" + "ახლა თქვენ შეძლებთ შეტყობინებების წაკითხვას ან გაგზავნას უსაფრთხოდ, სხვა მომხმარებლებსაც შეუძლიათ ამ მოწყობილობას ენდონ." + "მოწყობილობა დადასტურებულია" + "ველოდებით სხვა მოწყობილობას…" "რაღაცა არასწორადაა. ან მოთხოვნის ვადაა ამოწურული, ან მოთხოვნა უარყოფილი იყო." "დაადასტურეთ, რომ ქვემოთ მოყვანილი ემოჯიები შეესაბამება თქვენს სხვა სესიაზე ნაჩვენებს." "შეადარეთ ემოჯიები" "დაადასტურეთ, რომ ქვემოთ მოცემული ნომრები ემთხვევა თქვენს სხვა სესიაზე ნაჩვენები ნომრებს." "შეადარეთ რიცხვები" "თქვენი ახალი სესია დადასტურებულია. მას აქვს წვდომა დაშიფრულ შეტყობინებებზე და სხვა მომხმარებლები მას სანდოდ ხედავენ." + "შეიყვანეთ აღდგენის გასაღები" "დაამტკიცეთ, რომ ეს თქვენ ხართ, რათა მიიღოთ წვდომა თქვენი დაშიფრული შეტყობინებების ისტორიასთან." "არსებული სესიის გახსნა" "დადასტურების ხელახლა ცდა" @@ -13,6 +20,7 @@ "ველოდებით დამთხვევას" "შეადარეთ ემოციების უნიკალური ნაკრები." "შეადარეთ უნიკალური ემოჯი, დარწმუნდით, რომ ისინი ერთი დ იმავე თანმიმდევრობით გამოჩნდნენ." + "მოწყობილობა დადასტურებულია" "ისინი არ ემთხვევიან ერთმანეთს" "ისინი ემთხვევიან ერთმანეთს" "მიიღეთ დადასტურების მოთხოვნა თქვენს სხვა სესიაში ამ პროცესის გასაგრძელებლად." 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 8b2e5fad70..88148f2a0a 100644 --- a/features/verifysession/impl/src/main/res/values-nb/translations.xml +++ b/features/verifysession/impl/src/main/res/values-nb/translations.xml @@ -13,6 +13,7 @@ "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." "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." @@ -27,6 +28,7 @@ "Sammenlign de unike emojiene, og sørg for at de vises i samme rekkefølge." "Logget inn" "Enten ble forespørselen tidsavbrutt, forespørselen ble avslått eller det var en feil i verifiseringen." + "Verifisering mislyktes" "Bare fortsett hvis du startet denne verifiseringen." "Bekreft den andre enheten for å holde meldingsloggen din sikker." "Nå kan du lese eller sende meldinger sikkert på den andre enheten din." @@ -34,6 +36,15 @@ "Verifisering forespurt" "De matcher ikke" "De matcher" + "Sørg for at du har appen åpen på den andre enheten før du starter verifiseringen herfra." + "Åpne appen på en annen bekreftet enhet" + "For ekstra sikkerhet, verifiser denne brukeren ved å sammenligne et sett med emojier på enhetene dine. Gjør dette ved å bruke en pålitelig måte å kommunisere på." + "Vil du verifisere denne brukeren?" + "Du skal se en popup på den andre enheten. Start bekreftelsen derfra nå." + "Start verifiseringen på den andre enheten" + "Venter 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." "Venter på å godta forespørselen" "Logger ut…" 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 f95acc7347..0fd66a82d3 100644 --- a/features/verifysession/impl/src/main/res/values-sv/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sv/translations.xml @@ -13,9 +13,11 @@ "Något verkar inte stämma. Antingen gick tidsgränsen för begäran ut eller så avvisades begäran." "Bekräfta att emojierna nedan matchar de som visas på din andra session." "Jämför emojis" + "Bekräfta att emojierna nedan matchar de som visas på den andra användarens enhet." "Bekräfta att siffrorna nedan matchar de som visas på din andra session." "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." "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." @@ -37,8 +39,14 @@ "De matchar" "Se till att du har appen öppen på den andra enheten innan du startar verifieringen härifrån." "Öppna appen på en annan verifierad enhet" + "För extra säkerhet, verifiera den här användaren genom att jämföra en uppsättning emojier på dina enheter. Gör detta med hjälp av ett betrott kommunikationssätt." + "Verifiera den här användaren?" + "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" + "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." "Väntar på att acceptera begäran" "Loggar ut …" 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 45038bfe60..10c6ae184d 100644 --- a/features/verifysession/impl/src/main/res/values-uk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uk/translations.xml @@ -39,6 +39,7 @@ "Вони збігаються" "Перш ніж починати перевірку звідси, переконайтеся, що програму відкрито на іншому пристрої." "Відкрийте додаток на іншому перевіреному пристрої" + "Для додаткової безпеки верифікуйте цього користувача, порівнявши набір емоджі на ваших пристроях. Зробіть це, використовуючи надійний спосіб спілкування." "Верифікувати цього користувача?" "Для додаткової безпеки інший користувач хоче верифікувати вашу особистість. Вам буде показано набір емоджі для порівняння." "Ви повинні побачити спливаюче вікно на іншому пристрої. Почніть перевірку звідти." 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 82dc0acd35..46e26a2e70 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 @@ -13,9 +13,11 @@ "似乎出了一點問題。有可能是因為等候逾時,或是請求被拒絕。" "確認顯示在其他工作階段上的表情符號是否和下方的相同。" "比對表情符號" + "確認下方的表情符號與其他使用者裝置上顯示的表情符號相符。" "確認以下數字是否與其他作業階段中顯示的數字相符。" "比較數字" "新的工作階段已完成驗證。它能夠存取您的加密訊息,而其他使用者會將它視為可信任的。" + "現在,您可以在傳送或接收訊息時信任此使用者的身份。" "輸入復原金鑰" "請求逾時、請求被拒或是驗證不符。" "為了存取被加密的歷史訊息,您需要證明這是您本人。" @@ -37,8 +39,14 @@ "一樣" "從這裡開始驗證之前,請確保您在其他裝置中開啟了應用程式。" "在另外一個已驗證的裝置上開啟應用程式" + "為了提昇安全性,請透過比較您裝置上的一組表情符號來驗證此使用者。請透過可信的通訊方式來執行此動作。" + "驗證此使用者?" + "為了提昇安全性,另一個使用者希望驗證您的身份。您將會看到一組表情符號以進行比較。" "您應該會在其他裝置上看到一個彈出式視窗。立刻從那裡開始驗證。" "在其他裝置上開始驗證" + "正在等待其他裝置" + "正在等帶齊他使用者" + "接受後,您就可以繼續進行驗證。" "準備開始驗證,請到您的其他工作階段接受請求。" "等待接受請求" "正在登出…" 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 74b62a5654..73068bcaaf 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 @@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.core.FlowId 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 +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.test.A_USER_ID @@ -25,10 +26,13 @@ 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.advanceTimeBy import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test +import kotlin.time.Duration.Companion.seconds @ExperimentalCoroutinesApi class IncomingVerificationPresenterTest { @@ -37,7 +41,7 @@ class IncomingVerificationPresenterTest { @Test fun `present - nominal case - incoming verification successful`() = runTest { - val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } val acceptVerificationRequestLambda = lambdaRecorder { } val approveVerificationLambda = lambdaRecorder { } val resetLambda = lambdaRecorder { } @@ -59,15 +63,20 @@ class IncomingVerificationPresenterTest { isWaiting = false, ) ) + + advanceTimeBy(1.seconds) + resetLambda.assertions().isCalledOnce().with(value(false)) - acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification initialState.eventSink(IncomingVerificationViewEvents.StartVerification) skipItems(1) val initialWaitingState = awaitItem() assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() - advanceUntilIdle() + + advanceTimeBy(1.seconds) + acceptVerificationRequestLambda.assertions().isCalledOnce() // Remote sent the data fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) @@ -100,7 +109,7 @@ class IncomingVerificationPresenterTest { @Test fun `present - emoji not matching case - incoming verification failure`() = runTest { - val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } val acceptVerificationRequestLambda = lambdaRecorder { } val declineVerificationLambda = lambdaRecorder { } val resetLambda = lambdaRecorder { } @@ -123,7 +132,7 @@ class IncomingVerificationPresenterTest { ) ) resetLambda.assertions().isCalledOnce().with(value(false)) - acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification initialState.eventSink(IncomingVerificationViewEvents.StartVerification) @@ -157,7 +166,7 @@ class IncomingVerificationPresenterTest { @Test fun `present - incoming verification is remotely canceled`() = runTest { - val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } val acceptVerificationRequestLambda = lambdaRecorder { } val declineVerificationLambda = lambdaRecorder { } val resetLambda = lambdaRecorder { } @@ -185,13 +194,16 @@ class IncomingVerificationPresenterTest { fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidCancel) // The screen is dismissed skipItems(2) + + advanceUntilIdle() + onFinishLambda.assertions().isCalledOnce() } } @Test fun `present - user goes back when comparing emoji - incoming verification failure`() = runTest { - val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } val acceptVerificationRequestLambda = lambdaRecorder { } val declineVerificationLambda = lambdaRecorder { } val resetLambda = lambdaRecorder { } @@ -213,15 +225,20 @@ class IncomingVerificationPresenterTest { isWaiting = false, ) ) + + advanceTimeBy(1.seconds) + resetLambda.assertions().isCalledOnce().with(value(false)) - acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(aSessionVerificationRequestDetails)) + acknowledgeVerificationRequestLambda.assertions().isCalledOnce().with(value(anIncomingSessionVerificationRequest)) acceptVerificationRequestLambda.assertions().isNeverCalled() // User accept the incoming verification initialState.eventSink(IncomingVerificationViewEvents.StartVerification) skipItems(1) val initialWaitingState = awaitItem() assertThat((initialWaitingState.step as IncomingVerificationState.Step.Initial).isWaiting).isTrue() - advanceUntilIdle() + + advanceTimeBy(1.seconds) + acceptVerificationRequestLambda.assertions().isCalledOnce() // Remote sent the data fakeSessionVerificationService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) @@ -248,7 +265,7 @@ class IncomingVerificationPresenterTest { @Test fun `present - user ignores incoming request`() = runTest { - val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } + val acknowledgeVerificationRequestLambda = lambdaRecorder { _ -> } val acceptVerificationRequestLambda = lambdaRecorder { } val resetLambda = lambdaRecorder { } val fakeSessionVerificationService = FakeSessionVerificationService( @@ -268,24 +285,30 @@ class IncomingVerificationPresenterTest { } } - private val aSessionVerificationRequestDetails = SessionVerificationRequestDetails( - senderId = A_USER_ID, - flowId = FlowId("flowId"), - deviceId = A_DEVICE_ID, - displayName = "a device name", - firstSeenTimestamp = A_TIMESTAMP, + private val anIncomingSessionVerificationRequest = VerificationRequest.Incoming.OtherSession( + details = SessionVerificationRequestDetails( + senderProfile = SessionVerificationRequestDetails.SenderProfile( + userId = A_USER_ID, + displayName = "a device name", + avatarUrl = null, + ), + flowId = FlowId("flowId"), + deviceId = A_DEVICE_ID, + firstSeenTimestamp = A_TIMESTAMP, + ) ) - private fun createPresenter( - sessionVerificationRequestDetails: SessionVerificationRequestDetails = aSessionVerificationRequestDetails, + private fun TestScope.createPresenter( + verificationRequest: VerificationRequest.Incoming = anIncomingSessionVerificationRequest, navigator: IncomingVerificationNavigator = IncomingVerificationNavigator { lambdaError() }, service: SessionVerificationService = FakeSessionVerificationService(), dateFormatter: DateFormatter = FakeDateFormatter(), ) = IncomingVerificationPresenter( - sessionVerificationRequestDetails = sessionVerificationRequestDetails, + verificationRequest = verificationRequest, navigator = navigator, sessionVerificationService = service, stateMachine = IncomingVerificationStateMachine(service), dateFormatter = dateFormatter, + sessionCoroutineScope = backgroundScope, ) } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt index 435e2e688c..a9cd40c6f3 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionPresenterTest.kt @@ -9,27 +9,21 @@ package io.element.android.features.verifysession.impl.outgoing import app.cash.turbine.ReceiveTurbine import com.google.common.truth.Truth.assertThat -import io.element.android.features.logout.api.LogoutUseCase -import io.element.android.features.logout.test.FakeLogoutUseCase import io.element.android.features.verifysession.impl.outgoing.VerifySelfSessionState.Step import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.matrix.api.core.UserId 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.verification.SessionVerificationData -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.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationFlowState -import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService -import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -48,85 +42,70 @@ class VerifySelfSessionPresenterTest { ) presenter.test { awaitItem().run { - assertThat(step).isEqualTo(Step.Initial(false)) - assertThat(displaySkipButton).isTrue() + assertThat(step).isEqualTo(Step.Initial) } } } @Test - fun `present - hides skip verification button on non-debuggable builds`() = runTest { - val buildMeta = aBuildMeta(isDebuggable = false) - val presenter = createVerifySelfSessionPresenter( - service = unverifiedSessionService(), - buildMeta = buildMeta, - ) - presenter.test { - assertThat(awaitItem().displaySkipButton).isFalse() - } - } - - @Test - fun `present - Initial state is received, can use recovery key`() = runTest { - val resetLambda = lambdaRecorder { } - val presenter = createVerifySelfSessionPresenter( - service = unverifiedSessionService( - resetLambda = resetLambda - ), - encryptionService = FakeEncryptionService().apply { - emitRecoveryState(RecoveryState.INCOMPLETE) - } - ) - presenter.test { - assertThat(awaitItem().step).isEqualTo(Step.Initial(true)) - resetLambda.assertions().isCalledOnce().with(value(true)) - } - } - - @Test - fun `present - Initial state is received, can use recovery key and is last device`() = runTest { - val presenter = createVerifySelfSessionPresenter( - service = unverifiedSessionService(), - encryptionService = FakeEncryptionService().apply { - emitIsLastDevice(true) - emitRecoveryState(RecoveryState.INCOMPLETE) - } - ) - presenter.test { - assertThat(awaitItem().step).isEqualTo(Step.Initial(canEnterRecoveryKey = true, isLastDevice = true)) - } - } - - @Test - fun `present - Handles requestVerification`() = runTest { + fun `present - Handles requestVerification for session verification`() = runTest { + val requestSessionVerificationRecorder = lambdaRecorder {} + val startVerificationRecorder = lambdaRecorder {} val service = unverifiedSessionService( - requestVerificationLambda = { }, - startVerificationLambda = { }, + requestSessionVerificationLambda = requestSessionVerificationRecorder, + startVerificationLambda = startVerificationRecorder, + ) + val presenter = createVerifySelfSessionPresenter( + service = service, + verificationRequest = anOutgoingSessionVerificationRequest(), ) - val presenter = createVerifySelfSessionPresenter(service) presenter.test { requestVerificationAndAwaitVerifyingState(service) + + requestSessionVerificationRecorder.assertions().isCalledOnce() + startVerificationRecorder.assertions().isCalledOnce() } } @Test - fun `present - Cancellation on initial state does nothing`() = runTest { + fun `present - Handles requestVerification for user verification`() = runTest { + val requestUserVerificationRecorder = lambdaRecorder {} + val startVerificationRecorder = lambdaRecorder {} + val service = unverifiedSessionService( + requestUserVerificationLambda = requestUserVerificationRecorder, + startVerificationLambda = startVerificationRecorder, + ) + val presenter = createVerifySelfSessionPresenter( + service = service, + verificationRequest = anOutgoingUserVerificationRequest(), + ) + presenter.test { + requestVerificationAndAwaitVerifyingState(service) + + requestUserVerificationRecorder.assertions().isCalledOnce() + startVerificationRecorder.assertions().isCalledOnce() + } + } + + @Test + fun `present - Cancellation on initial state moves to Exit state`() = runTest { val presenter = createVerifySelfSessionPresenter( service = unverifiedSessionService(), ) presenter.test { val initialState = awaitItem() - assertThat(initialState.step).isEqualTo(Step.Initial(false)) + assertThat(initialState.step).isEqualTo(Step.Initial) val eventSink = initialState.eventSink eventSink(VerifySelfSessionViewEvents.Cancel) - expectNoEvents() + + assertThat(awaitItem().step).isEqualTo(Step.Exit) } } @Test fun `present - A failure when verifying cancels it`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, approveVerificationLambda = { }, ) @@ -143,24 +122,23 @@ class VerifySelfSessionPresenterTest { } @Test - fun `present - A fail when requesting verification resets the state to the initial one`() = runTest { + fun `present - A fail when requesting verification resets the state to the canceled one`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) presenter.test { - awaitItem().eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) awaitItem().eventSink(VerifySelfSessionViewEvents.RequestVerification) service.emitVerificationFlowState(VerificationFlowState.DidFail) assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) - assertThat(awaitItem().step).isEqualTo(Step.Initial(false)) + assertThat(awaitItem().step).isEqualTo(Step.Canceled) } } @Test fun `present - Canceling the flow once it's verifying cancels it`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, cancelVerificationLambda = { }, ) @@ -175,7 +153,7 @@ class VerifySelfSessionPresenterTest { @Test fun `present - When verifying, if we receive another challenge we ignore it`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) @@ -189,7 +167,7 @@ class VerifySelfSessionPresenterTest { @Test fun `present - Go back after cancellation returns to initial state`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, ) val presenter = createVerifySelfSessionPresenter(service) @@ -199,7 +177,7 @@ class VerifySelfSessionPresenterTest { assertThat(awaitItem().step).isEqualTo(Step.Canceled) state.eventSink(VerifySelfSessionViewEvents.Reset) // Went back to initial state - assertThat(awaitItem().step).isEqualTo(Step.Initial(false)) + assertThat(awaitItem().step).isEqualTo(Step.Initial) cancelAndIgnoreRemainingEvents() } } @@ -207,10 +185,10 @@ class VerifySelfSessionPresenterTest { @Test fun `present - When verification is approved, the flow completes if there is no error`() = runTest { val emojis = listOf( - VerificationEmoji(number = 30, emoji = "😀", description = "Smiley") + VerificationEmoji(number = 30) ) val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, approveVerificationLambda = { }, ) @@ -235,7 +213,7 @@ class VerifySelfSessionPresenterTest { @Test fun `present - When verification is declined, the flow is canceled`() = runTest { val service = unverifiedSessionService( - requestVerificationLambda = { }, + requestSessionVerificationLambda = { }, startVerificationLambda = { }, declineVerificationLambda = { }, ) @@ -254,20 +232,6 @@ class VerifySelfSessionPresenterTest { } } - @Test - fun `present - Skip event skips the flow`() = runTest { - val service = unverifiedSessionService( - requestVerificationLambda = { }, - startVerificationLambda = { }, - ) - val presenter = createVerifySelfSessionPresenter(service) - presenter.test { - val state = requestVerificationAndAwaitVerifyingState(service) - state.eventSink(VerifySelfSessionViewEvents.SkipVerification) - assertThat(awaitItem().step).isEqualTo(Step.Skipped) - } - } - @Test fun `present - When verification is done using recovery key, the flow is completed`() = runTest { val service = FakeSessionVerificationService( @@ -301,32 +265,7 @@ class VerifySelfSessionPresenterTest { ) presenter.test { skipItems(1) - assertThat(awaitItem().step).isEqualTo(Step.Skipped) - } - } - - @Test - fun `present - When user request to sign out, the sign out use case is invoked`() = runTest { - val service = FakeSessionVerificationService( - resetLambda = { }, - ).apply { - emitNeedsSessionVerification(false) - emitVerifiedStatus(SessionVerifiedStatus.Verified) - emitVerificationFlowState(VerificationFlowState.DidFinish) - } - val signOutLambda = lambdaRecorder {} - val presenter = createVerifySelfSessionPresenter( - service, - logoutUseCase = FakeLogoutUseCase(signOutLambda) - ) - presenter.test { - skipItems(1) - val initialItem = awaitItem() - initialItem.eventSink(VerifySelfSessionViewEvents.SignOut) - assertThat(awaitItem().signOutAction.isLoading()).isTrue() - val finalItem = awaitItem() - assertThat(finalItem.signOutAction.isSuccess()).isTrue() - signOutLambda.assertions().isCalledOnce().with(value(true)) + assertThat(awaitItem().step).isEqualTo(Step.Exit) } } @@ -335,10 +274,7 @@ class VerifySelfSessionPresenterTest { sessionVerificationData: SessionVerificationData = SessionVerificationData.Emojis(emptyList()), ): VerifySelfSessionState { var state = awaitItem() - assertThat(state.step).isEqualTo(Step.Initial(false)) - state.eventSink(VerifySelfSessionViewEvents.UseAnotherDevice) - state = awaitItem() - assertThat(state.step).isEqualTo(Step.UseAnotherDevice) + assertThat(state.step).isEqualTo(Step.Initial) state.eventSink(VerifySelfSessionViewEvents.RequestVerification) // Await for other device response: fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) @@ -360,17 +296,19 @@ class VerifySelfSessionPresenterTest { } private suspend fun unverifiedSessionService( - requestVerificationLambda: () -> Unit = { lambdaError() }, + requestSessionVerificationLambda: () -> Unit = { lambdaError() }, + requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() }, cancelVerificationLambda: () -> Unit = { lambdaError() }, approveVerificationLambda: () -> Unit = { lambdaError() }, declineVerificationLambda: () -> Unit = { lambdaError() }, startVerificationLambda: () -> Unit = { lambdaError() }, resetLambda: (Boolean) -> Unit = { }, - acknowledgeVerificationRequestLambda: (SessionVerificationRequestDetails) -> Unit = { lambdaError() }, + acknowledgeVerificationRequestLambda: (VerificationRequest.Incoming) -> Unit = { lambdaError() }, acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, ): FakeSessionVerificationService { return FakeSessionVerificationService( - requestVerificationLambda = requestVerificationLambda, + requestCurrentSessionVerificationLambda = requestSessionVerificationLambda, + requestUserVerificationLambda = requestUserVerificationLambda, cancelVerificationLambda = cancelVerificationLambda, approveVerificationLambda = approveVerificationLambda, declineVerificationLambda = declineVerificationLambda, @@ -385,20 +323,15 @@ class VerifySelfSessionPresenterTest { private fun createVerifySelfSessionPresenter( service: SessionVerificationService, + verificationRequest: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), encryptionService: EncryptionService = FakeEncryptionService(), - buildMeta: BuildMeta = aBuildMeta(), - sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(), - logoutUseCase: LogoutUseCase = FakeLogoutUseCase(), showDeviceVerifiedScreen: Boolean = false, ): VerifySelfSessionPresenter { return VerifySelfSessionPresenter( showDeviceVerifiedScreen = showDeviceVerifiedScreen, + verificationRequest = verificationRequest, sessionVerificationService = service, encryptionService = encryptionService, - stateMachine = VerifySelfSessionStateMachine(service, encryptionService), - buildMeta = buildMeta, - sessionPreferencesStore = sessionPreferencesStore, - logoutUseCase = logoutUseCase, ) } } diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt index 9eb8724152..c24809c31e 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/VerifySelfSessionViewTest.kt @@ -16,7 +16,6 @@ import io.element.android.features.verifysession.impl.ui.aEmojisSessionVerificat import io.element.android.libraries.architecture.AsyncData 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 @@ -25,7 +24,6 @@ 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 VerifySelfSessionViewTest { @@ -103,16 +101,16 @@ class VerifySelfSessionViewTest { } @Test - fun `back key pressed - on Completed step does nothing`() { - val eventsRecorder = EventsRecorder() - rule.setVerifySelfSessionView( - aVerifySelfSessionState( - step = VerifySelfSessionState.Step.Completed, - eventSink = eventsRecorder - ), - ) - rule.pressBackKey() - eventsRecorder.assertEmpty() + fun `back key pressed - on Completed exits the flow`() { + ensureCalledOnce { callback -> + rule.setVerifySelfSessionView( + onBack = callback, + state = aVerifySelfSessionState( + step = VerifySelfSessionState.Step.Completed, + ), + ) + rule.pressBackKey() + } } @Test @@ -130,38 +128,6 @@ class VerifySelfSessionViewTest { } } - @Config(qualifiers = "h1024dp") - @Test - fun `clicking on enter recovery key calls the expected callback`() { - val eventsRecorder = EventsRecorder(expectEvents = false) - ensureCalledOnce { callback -> - rule.setVerifySelfSessionView( - aVerifySelfSessionState( - step = VerifySelfSessionState.Step.Initial(true), - eventSink = eventsRecorder - ), - onEnterRecoveryKey = callback, - ) - rule.clickOn(R.string.screen_session_verification_enter_recovery_key) - } - } - - @Config(qualifiers = "h1024dp") - @Test - fun `clicking on learn more invokes the expected callback`() { - val eventsRecorder = EventsRecorder(expectEvents = false) - ensureCalledOnce { callback -> - rule.setVerifySelfSessionView( - aVerifySelfSessionState( - step = VerifySelfSessionState.Step.Initial(true), - eventSink = eventsRecorder - ), - onLearnMoreClick = callback, - ) - rule.clickOn(CommonStrings.action_learn_more) - } - } - @Test fun `clicking on they match emits the expected event`() { val eventsRecorder = EventsRecorder() @@ -194,48 +160,18 @@ class VerifySelfSessionViewTest { eventsRecorder.assertSingle(VerifySelfSessionViewEvents.DeclineVerification) } - @Test - fun `clicking on 'Skip' emits the expected event`() { - val eventsRecorder = EventsRecorder() - rule.setVerifySelfSessionView( - aVerifySelfSessionState( - step = VerifySelfSessionState.Step.Initial(canEnterRecoveryKey = true), - displaySkipButton = true, - eventSink = eventsRecorder - ), - ) - rule.clickOn(CommonStrings.action_skip) - eventsRecorder.assertSingle(VerifySelfSessionViewEvents.SkipVerification) - } - - @Test - fun `on Skipped step - onFinished callback is called immediately`() { - ensureCalledOnce { callback -> - rule.setVerifySelfSessionView( - aVerifySelfSessionState( - step = VerifySelfSessionState.Step.Skipped, - displaySkipButton = true, - eventSink = EnsureNeverCalledWithParam(), - ), - onFinished = callback, - ) - } - } - private fun AndroidComposeTestRule.setVerifySelfSessionView( state: VerifySelfSessionState, onLearnMoreClick: () -> Unit = EnsureNeverCalled(), - onEnterRecoveryKey: () -> Unit = EnsureNeverCalled(), onFinished: () -> Unit = EnsureNeverCalled(), - onResetKey: () -> Unit = EnsureNeverCalled(), + onBack: () -> Unit = EnsureNeverCalled(), ) { setContent { VerifySelfSessionView( state = state, onLearnMoreClick = onLearnMoreClick, - onEnterRecoveryKey = onEnterRecoveryKey, onFinish = onFinished, - onResetKey = onResetKey, + onBack = onBack, ) } } diff --git a/features/viewfolder/api/build.gradle.kts b/features/viewfolder/api/build.gradle.kts index b24ad7490e..4df696b2b2 100644 --- a/features/viewfolder/api/build.gradle.kts +++ b/features/viewfolder/api/build.gradle.kts @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") } android { diff --git a/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.kt new file mode 100644 index 0000000000..fa1d3511e4 --- /dev/null +++ b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/TextFileViewer.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.features.viewfolder.api + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import kotlinx.collections.immutable.ImmutableList + +fun interface TextFileViewer { + @Composable + fun Render( + lines: ImmutableList, + modifier: Modifier, + ) +} diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt new file mode 100644 index 0000000000..55743f54cc --- /dev/null +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt @@ -0,0 +1,33 @@ +/* + * 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.viewfolder.impl + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.features.viewfolder.impl.file.ColorationMode +import io.element.android.features.viewfolder.impl.file.FileContent +import io.element.android.libraries.di.AppScope +import kotlinx.collections.immutable.ImmutableList +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultTextFileViewer @Inject constructor() : TextFileViewer { + @Composable + override fun Render( + lines: ImmutableList, + modifier: Modifier + ) { + FileContent( + lines = lines, + colorationMode = ColorationMode.None, + modifier = modifier + ) + } +} diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt new file mode 100644 index 0000000000..8a233d4506 --- /dev/null +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt @@ -0,0 +1,152 @@ +/* + * 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.viewfolder.impl.file + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.androidutils.system.copyToClipboard +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList + +@Composable +internal fun FileContent( + lines: ImmutableList, + colorationMode: ColorationMode, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier + ) { + if (lines.isEmpty()) { + item { + Spacer(Modifier.size(80.dp)) + Text( + text = stringResource(CommonStrings.common_empty_file), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.fillMaxWidth() + ) + } + } else { + itemsIndexed( + items = lines, + ) { index, line -> + LineRow( + lineNumber = index + 1, + line = line, + colorationMode = colorationMode, + ) + } + } + } +} + +@Composable +private fun LineRow( + lineNumber: Int, + line: String, + colorationMode: ColorationMode, +) { + val context = LocalContext.current + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = { + context.copyToClipboard( + text = line, + toastMessage = context.getString(CommonStrings.common_line_copied_to_clipboard), + ) + }) + ) { + Text( + modifier = Modifier + .widthIn(min = 36.dp) + .padding(horizontal = 4.dp), + text = "$lineNumber", + textAlign = TextAlign.End, + color = ElementTheme.colors.textSecondary, + style = ElementTheme.typography.fontBodyMdMedium, + ) + val color = ElementTheme.colors.textSecondary + val width = 0.5.dp.value + Text( + modifier = Modifier + .weight(1f) + .drawWithContent { + // Using .height(IntrinsicSize.Min) on the Row does not work well inside LazyColumn + drawLine( + color = color, + start = Offset(0f, 0f), + end = Offset(0f, size.height), + strokeWidth = width + ) + drawContent() + } + .padding(horizontal = 4.dp), + text = line, + color = line.toColor(colorationMode), + style = ElementTheme.typography.fontBodyMdRegular + ) + } +} + +/** + * Convert a line to a color. + * Ex for logcat: + * `01-23 13:14:50.740 25818 25818 D org.matrix.rust.sdk: elementx: SyncIndicator = Hide | RustRoomListService.kt:81` + * ^ use this char to determine the color + * Ex for Rust logs: + * `2024-01-26T10:22:26.947416Z WARN elementx: Restore with non-empty map | MatrixClientsHolder.kt:68` + * ^ use this char to determine the color, see [LogLevel] + */ +@Composable +private fun String.toColor(colorationMode: ColorationMode): Color { + return when (colorationMode) { + ColorationMode.Logcat -> when (getOrNull(31)) { + 'D' -> colorDebug + 'I' -> colorInfo + 'W' -> colorWarning + 'E' -> colorError + 'A' -> colorError + else -> ElementTheme.colors.textPrimary + } + ColorationMode.RustLogs -> when (getOrNull(32)) { + 'E' -> ElementTheme.colors.textPrimary + 'G' -> colorDebug + 'O' -> colorInfo + 'N' -> colorWarning + 'R' -> colorError + else -> ElementTheme.colors.textPrimary + } + ColorationMode.None -> ElementTheme.colors.textPrimary + } +} + +private val colorDebug = Color(0xFF299999) +private val colorInfo = Color(0xFFABC023) +private val colorWarning = Color(0xFFBBB529) +private val colorError = Color(0xFFFF6B68) diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt index bae8166f49..3851a4a554 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt @@ -7,32 +7,16 @@ package io.element.android.features.viewfolder.impl.file -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext 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.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.components.async.AsyncFailure import io.element.android.libraries.designsystem.components.async.AsyncLoading @@ -46,7 +30,6 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @OptIn(ExperimentalMaterial3Api::class) @@ -114,125 +97,6 @@ fun ViewFileView( ) } -@Composable -private fun FileContent( - lines: ImmutableList, - colorationMode: ColorationMode, - modifier: Modifier = Modifier, -) { - LazyColumn( - modifier = modifier - ) { - if (lines.isEmpty()) { - item { - Spacer(Modifier.size(80.dp)) - Text( - text = "Empty file", - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.tertiary, - modifier = Modifier.fillMaxWidth() - ) - } - } else { - itemsIndexed( - items = lines, - ) { index, line -> - LineRow( - lineNumber = index + 1, - line = line, - colorationMode = colorationMode, - ) - } - } - } -} - -@Composable -private fun LineRow( - lineNumber: Int, - line: String, - colorationMode: ColorationMode, -) { - val context = LocalContext.current - Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = { - context.copyToClipboard( - line, - "Line copied to clipboard", - ) - }) - ) { - Text( - modifier = Modifier - .widthIn(min = 36.dp) - .padding(horizontal = 4.dp), - text = "$lineNumber", - textAlign = TextAlign.End, - color = ElementTheme.colors.textSecondary, - style = ElementTheme.typography.fontBodyMdMedium, - ) - val color = ElementTheme.colors.textSecondary - val width = 0.5.dp.value - Text( - modifier = Modifier - .weight(1f) - .drawWithContent { - // Using .height(IntrinsicSize.Min) on the Row does not work well inside LazyColumn - drawLine( - color = color, - start = Offset(0f, 0f), - end = Offset(0f, size.height), - strokeWidth = width - ) - drawContent() - } - .padding(horizontal = 4.dp), - text = line, - color = line.toColor(colorationMode), - style = ElementTheme.typography.fontBodyMdRegular - ) - } -} - -/** - * Convert a line to a color. - * Ex for logcat: - * `01-23 13:14:50.740 25818 25818 D org.matrix.rust.sdk: elementx: SyncIndicator = Hide | RustRoomListService.kt:81` - * ^ use this char to determine the color - * Ex for Rust logs: - * `2024-01-26T10:22:26.947416Z WARN elementx: Restore with non-empty map | MatrixClientsHolder.kt:68` - * ^ use this char to determine the color, see [LogLevel] - */ -@Composable -private fun String.toColor(colorationMode: ColorationMode): Color { - return when (colorationMode) { - ColorationMode.Logcat -> when (getOrNull(31)) { - 'D' -> colorDebug - 'I' -> colorInfo - 'W' -> colorWarning - 'E' -> colorError - 'A' -> colorError - else -> ElementTheme.colors.textPrimary - } - ColorationMode.RustLogs -> when (getOrNull(32)) { - 'E' -> ElementTheme.colors.textPrimary - 'G' -> colorDebug - 'O' -> colorInfo - 'N' -> colorWarning - 'R' -> colorError - else -> ElementTheme.colors.textPrimary - } - ColorationMode.None -> ElementTheme.colors.textPrimary - } -} - -private val colorDebug = Color(0xFF299999) -private val colorInfo = Color(0xFFABC023) -private val colorWarning = Color(0xFFBBB529) -private val colorError = Color(0xFFFF6B68) - @PreviewsDayNight @Composable internal fun ViewFileViewPreview(@PreviewParameter(ViewFileStateProvider::class) state: ViewFileState) = ElementPreview { diff --git a/gradle.properties b/gradle.properties index fce836543a..4a941c0143 100644 --- a/gradle.properties +++ b/gradle.properties @@ -40,7 +40,7 @@ signing.element.nightly.keyPassword=Secret # Customise the Lint version to use a more recent version than the one bundled with AGP # https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html -android.experimental.lint.version=8.9.0-alpha08 +android.experimental.lint.version=8.10.0-alpha08 # Enable test fixture for all modules by default android.experimental.enableTestFixtures=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9f78c5e739..4df9edcd68 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ media3 = "1.5.1" camera = "1.4.1" # Compose -compose_bom = "2025.02.00" +compose_bom = "2025.03.00" composecompiler = "1.5.15" # Coroutines @@ -50,10 +50,10 @@ wysiwyg = "2.38.2" telephoto = "0.15.1" # Dependency analysis -dependencyAnalysis = "2.10.1" +dependencyAnalysis = "2.13.0" # DI -dagger = "2.55" +dagger = "2.56" anvil = "0.4.1" # Auto service @@ -81,6 +81,7 @@ google_firebase_bom = "com.google.firebase:firebase-bom:33.10.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } +google_tink = "com.google.crypto.tink:tink-android:1.17.0" # AndroidX androidx_core = { module = "androidx.core:core", version.ref = "core" } @@ -100,7 +101,6 @@ androidx_browser = "androidx.browser:browser:1.8.0" androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } androidx_splash = "androidx.core:core-splashscreen:1.0.1" -androidx_security_crypto = "androidx.security:security-crypto:1.1.0-alpha06" androidx_media3_exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } androidx_media3_ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } androidx_biometric = "androidx.biometric:biometric-ktx:1.2.0-alpha05" @@ -109,7 +109,7 @@ androidx_activity_activity = { module = "androidx.activity:activity", version.re androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx_startup = "androidx.startup:startup-runtime:1.2.0" androidx_preference = "androidx.preference:preference:1.2.1" -androidx_webkit = "androidx.webkit:webkit:1.12.1" +androidx_webkit = "androidx.webkit:webkit:1.13.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_material3 = { module = "androidx.compose.material3:material3" } @@ -174,7 +174,7 @@ jsoup = "org.jsoup:jsoup:1.19.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.3.6" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.3.20" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } @@ -188,15 +188,15 @@ 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.8.2" +maplibre = "org.maplibre.gl:android-sdk:11.8.3" 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.1.0" zxing_cpp = "io.github.zxing-cpp:android:2.3.0" # Analytics -posthog = "com.posthog:posthog-android:3.11.3" -sentry = "io.sentry:sentry-android:8.3.0" +posthog = "com.posthog:posthog-android:3.12.0" +sentry = "io.sentry:sentry-android:8.5.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" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530..9bbc975c74 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d71047787f..36e4933e1d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=8d97a97984f6cbd2b85fe4c60a743440a347544bf18818048e611f5288d46c94 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionSha256Sum=20f1b1176237254a6fc204d8434196fa11a4cfb387567519c61556e8710aed78 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b0d..faf93008b7 100755 --- a/gradlew +++ b/gradlew @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. diff --git a/libraries/androidutils/build.gradle.kts b/libraries/androidutils/build.gradle.kts index 02c8061aa2..1aa23a09e8 100644 --- a/libraries/androidutils/build.gradle.kts +++ b/libraries/androidutils/build.gradle.kts @@ -31,7 +31,6 @@ dependencies { implementation(libs.androidx.activity.activity) implementation(libs.androidx.recyclerview) implementation(libs.androidx.exifinterface) - implementation(libs.androidx.security.crypto) api(libs.androidx.browser) testImplementation(projects.tests.testutils) diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/EncryptedFileFactory.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/EncryptedFileFactory.kt deleted file mode 100644 index 64f88e5d50..0000000000 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/file/EncryptedFileFactory.kt +++ /dev/null @@ -1,30 +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.androidutils.file - -import android.content.Context -import androidx.security.crypto.EncryptedFile -import androidx.security.crypto.MasterKey -import java.io.File - -class EncryptedFileFactory( - private val context: Context, -) { - fun create(file: File): EncryptedFile { - // We need to use the same key for all the encrypted files. - val masterKey = MasterKey.Builder(context) - .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - .build() - return EncryptedFile.Builder( - context, - file, - masterKey, - EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB - ).build() - } -} diff --git a/libraries/androidutils/src/main/res/values-eu/translations.xml b/libraries/androidutils/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..3ea09b64b2 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-eu/translations.xml @@ -0,0 +1,4 @@ + + + "Ez da ekintza hau kudeatzeko aplikazio bateragarririk aurkitu." + diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/SuspendLazy.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/SuspendLazy.kt new file mode 100644 index 0000000000..e5442c4940 --- /dev/null +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/coroutine/SuspendLazy.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.libraries.core.coroutine + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.launch +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +fun suspendLazy(coroutineContext: CoroutineContext = EmptyCoroutineContext, block: suspend () -> T): Lazy> { + return lazy(LazyThreadSafetyMode.NONE) { + val deferred = CompletableDeferred() + CoroutineScope(coroutineContext).launch { + deferred.complete(block()) + } + deferred + } +} diff --git a/libraries/dateformatter/impl/src/main/res/values-es/translations.xml b/libraries/dateformatter/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..04d9da0d3b --- /dev/null +++ b/libraries/dateformatter/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s a las %2$s" + "Este mes" + diff --git a/libraries/dateformatter/impl/src/main/res/values-eu/translations.xml b/libraries/dateformatter/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..5e690a750e --- /dev/null +++ b/libraries/dateformatter/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,4 @@ + + + "Hilabete hau" + diff --git a/libraries/designsystem/build.gradle.kts b/libraries/designsystem/build.gradle.kts index bbb8efdadf..d95566ebb5 100644 --- a/libraries/designsystem/build.gradle.kts +++ b/libraries/designsystem/build.gradle.kts @@ -32,6 +32,7 @@ android { implementation(libs.coil.compose) implementation(libs.vanniktech.blurhash) implementation(projects.features.enterprise.api) + implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.libraries.preferences.api) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt index 47b43fea40..c059cfe880 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/pages/HeaderFooterPage.kt @@ -10,18 +10,21 @@ package io.element.android.libraries.designsystem.atomic.pages import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.consumeWindowInsets 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.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -import androidx.compose.runtime.movableContentOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.designsystem.preview.ElementPreview @@ -31,7 +34,7 @@ import io.element.android.libraries.designsystem.theme.components.Text /** * @param modifier Classical modifier. - * @param paddingValues padding values to apply to the content. + * @param contentPadding padding values to apply to the content. * @param containerColor color of the container. Set to [Color.Transparent] if you provide a background in the [modifier]. * @param isScrollable if the whole content should be scrollable. * @param background optional background component. @@ -44,7 +47,7 @@ import io.element.android.libraries.designsystem.theme.components.Text @Composable fun HeaderFooterPage( modifier: Modifier = Modifier, - paddingValues: PaddingValues = PaddingValues(20.dp), + contentPadding: PaddingValues = PaddingValues(20.dp), containerColor: Color = ElementTheme.colors.bgCanvasDefault, isScrollable: Boolean = false, background: @Composable () -> Unit = {}, @@ -53,64 +56,67 @@ fun HeaderFooterPage( footer: @Composable () -> Unit = {}, content: @Composable () -> Unit = {}, ) { - val topBar = remember { movableContentOf(topBar) } - val header = remember { movableContentOf(header) } - val footer = remember { movableContentOf(footer) } - val content = remember { movableContentOf(content) } Scaffold( modifier = modifier, topBar = topBar, containerColor = containerColor, - ) { padding -> + ) { insetsPadding -> + val layoutDirection = LocalLayoutDirection.current + val contentInsetsPadding = remember(insetsPadding, layoutDirection) { + PaddingValues( + start = insetsPadding.calculateStartPadding(layoutDirection), + top = insetsPadding.calculateTopPadding(), + end = insetsPadding.calculateEndPadding(layoutDirection), + ) + } + val footerInsetsPadding = remember(insetsPadding, layoutDirection) { + PaddingValues( + start = insetsPadding.calculateStartPadding(layoutDirection), + end = insetsPadding.calculateEndPadding(layoutDirection), + bottom = insetsPadding.calculateBottomPadding(), + ) + } Box { background() - if (isScrollable) { - // Render in a LazyColumn - LazyColumn( - modifier = Modifier - .padding(paddingValues = paddingValues) - .padding(padding) - .consumeWindowInsets(padding) - .imePadding() - ) { - // Header - item { - header() - } - // Content - item { - content() - } - // Footer - item { - Box(modifier = Modifier.padding(horizontal = 16.dp)) { - footer() - } - } - } - } else { - // Render in a Column + + // Render in a Column + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues = contentPadding) + .consumeWindowInsets(insetsPadding) + .imePadding(), + ) { + // Content Column( modifier = Modifier - .padding(paddingValues = paddingValues) - .padding(padding) - .consumeWindowInsets(padding) - .imePadding() + .fillMaxWidth() + .run { + if (isScrollable) { + verticalScroll(rememberScrollState()) + } else { + Modifier + } + } + // Apply insets here so if the content is scrollable it can get below the top app bar if needed + .padding(contentInsetsPadding) + .weight(1f), ) { // Header header() - // Content - Column( - modifier = Modifier - .weight(1f) - .fillMaxWidth(), - ) { + Box(modifier = Modifier.weight(1f)) { content() } - // Footer - Box(modifier = Modifier.padding(horizontal = 16.dp)) { - footer() - } + } + + // Footer + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .fillMaxWidth() + .padding(footerInsetsPadding) + ) { + footer() } } } @@ -123,8 +129,7 @@ internal fun HeaderFooterPagePreview() = ElementPreview { HeaderFooterPage( content = { Box( - Modifier - .fillMaxSize(), + modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center ) { Text( @@ -159,3 +164,46 @@ internal fun HeaderFooterPagePreview() = ElementPreview { } ) } + +@PreviewsDayNight +@Composable +internal fun HeaderFooterPageScrollablePreview() = ElementPreview { + HeaderFooterPage( + content = { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Content", + style = ElementTheme.typography.fontHeadingXlBold + ) + } + }, + header = { + Box( + Modifier + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Header", + style = ElementTheme.typography.fontHeadingXlBold + ) + } + }, + footer = { + Box( + Modifier + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = "Footer", + style = ElementTheme.typography.fontHeadingXlBold + ) + } + }, + isScrollable = true, + ) +} 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 c9a80a50d7..f469555ee4 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 @@ -33,6 +33,7 @@ import io.element.android.compound.theme.ElementTheme 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.CircularProgressIndicator import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.ui.strings.CommonStrings @@ -78,6 +79,11 @@ object BigIcon { * A success style with a tinted background. */ data object SuccessSolid : Style + + /** + * A loading style with the default background color. + */ + data object Loading : Style } /** @@ -101,31 +107,7 @@ object BigIcon { Style.Success -> Color.Transparent Style.AlertSolid -> ElementTheme.colors.bgCriticalSubtle Style.SuccessSolid -> ElementTheme.colors.bgSuccessSubtle - } - val icon = when (style) { - is Style.Default -> style.vectorIcon - Style.Alert, - Style.AlertSolid -> CompoundIcons.ErrorSolid() - Style.Success, - Style.SuccessSolid -> CompoundIcons.CheckCircleSolid() - } - val contentDescription = when (style) { - is Style.Default -> style.contentDescription - Style.Alert, - Style.AlertSolid -> stringResource(CommonStrings.common_error) - Style.Success, - Style.SuccessSolid -> stringResource(CommonStrings.common_success) - } - val iconTint = when (style) { - is Style.Default -> if (style.useCriticalTint) { - ElementTheme.colors.iconCriticalPrimary - } else { - ElementTheme.colors.iconSecondary - } - Style.Alert, - Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary - Style.Success, - Style.SuccessSolid -> ElementTheme.colors.iconSuccessPrimary + Style.Loading -> ElementTheme.colors.bgSubtleSecondary } Box( modifier = modifier @@ -134,12 +116,50 @@ object BigIcon { .background(backgroundColor), contentAlignment = Alignment.Center, ) { - Icon( - modifier = Modifier.size(32.dp), - tint = iconTint, - imageVector = icon, - contentDescription = contentDescription - ) + if (style is Style.Loading) { + CircularProgressIndicator( + modifier = Modifier.size(27.dp), + color = ElementTheme.colors.iconSecondary, + trackColor = Color.Transparent, + strokeWidth = 3.dp, + ) + } else { + val icon = when (style) { + is Style.Default -> style.vectorIcon + Style.Alert, + Style.AlertSolid -> CompoundIcons.ErrorSolid() + Style.Success, + Style.SuccessSolid -> CompoundIcons.CheckCircleSolid() + Style.Loading -> error("This should never be reached") + } + val contentDescription = when (style) { + is Style.Default -> style.contentDescription + Style.Alert, + Style.AlertSolid -> stringResource(CommonStrings.common_error) + Style.Success, + Style.SuccessSolid -> stringResource(CommonStrings.common_success) + Style.Loading -> error("This should never be reached") + } + val iconTint = when (style) { + is Style.Default -> if (style.useCriticalTint) { + ElementTheme.colors.iconCriticalPrimary + } else { + ElementTheme.colors.iconSecondary + } + Style.Alert, + Style.AlertSolid -> ElementTheme.colors.iconCriticalPrimary + Style.Success, + Style.SuccessSolid -> ElementTheme.colors.iconSuccessPrimary + Style.Loading -> error("This should never be reached") + } + + Icon( + modifier = Modifier.size(32.dp), + tint = iconTint, + imageVector = icon, + contentDescription = contentDescription + ) + } } } } @@ -173,6 +193,7 @@ internal class BigIconStyleProvider : PreviewParameterProvider { BigIcon.Style.AlertSolid, BigIcon.Style.Default(Icons.Filled.CatchingPokemon, useCriticalTint = true), BigIcon.Style.Success, - BigIcon.Style.SuccessSolid + BigIcon.Style.SuccessSolid, + BigIcon.Style.Loading, ) } 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 437b36235e..0f84b82fd1 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 @@ -61,4 +61,6 @@ enum class AvatarSize(val dp: Dp) { MediaSender(32.dp), DmCreationConfirmation(64.dp), + + UserVerification(52.dp), } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/GradientFloatingActionButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/GradientFloatingActionButton.kt index e693b6ad64..d58e181353 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/GradientFloatingActionButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/GradientFloatingActionButton.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.center +import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.LinearGradientShader import androidx.compose.ui.graphics.RadialGradientShader @@ -36,10 +37,12 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import io.element.android.compound.annotations.CoreColorToken +import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.internal.LightColorTokens 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.designsystem.theme.components.Icon @OptIn(CoreColorToken::class) @@ -50,6 +53,16 @@ fun GradientFloatingActionButton( shape: Shape = RoundedCornerShape(25), content: @Composable () -> Unit, ) { + val color1 = if (LocalBuildMeta.current.isEnterpriseBuild) { + ElementTheme.colors.textActionAccent + } else { + LightColorTokens.colorGreen700 + } + val color2 = if (LocalBuildMeta.current.isEnterpriseBuild) { + ElementTheme.colors.textActionAccent + } else { + LightColorTokens.colorBlue900 + } val linearShaderBrush = remember { object : ShaderBrush() { override fun createShader(size: Size): Shader { @@ -57,8 +70,8 @@ fun GradientFloatingActionButton( from = Offset(size.width, size.height), to = Offset(size.width, 0f), colors = listOf( - LightColorTokens.colorBlue900, - LightColorTokens.colorGreen700, + color2, + color1, ), ) } @@ -71,8 +84,8 @@ fun GradientFloatingActionButton( center = size.center, radius = size.width / 2, colors = listOf( - LightColorTokens.colorGreen700, - LightColorTokens.colorBlue900, + color1, + color2, ) ) } @@ -85,8 +98,8 @@ fun GradientFloatingActionButton( .graphicsLayer(shape = shape, clip = false) .clip(shape) .drawBehind { - drawRect(brush = radialShaderBrush, alpha = 0.4f) drawRect(brush = linearShaderBrush) + drawRect(brush = radialShaderBrush, alpha = 0.4f, blendMode = BlendMode.Overlay) } .clickable( enabled = true, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/SuperButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/SuperButton.kt index d9e9471531..a53955afc2 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/SuperButton.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/button/SuperButton.kt @@ -42,6 +42,7 @@ import io.element.android.compound.tokens.generated.internal.DarkColorTokens import io.element.android.compound.tokens.generated.internal.LightColorTokens 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.designsystem.theme.components.ButtonSize import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.lowHorizontalPaddingValue @@ -66,17 +67,24 @@ fun SuperButton( } } val isLightTheme = ElementTheme.isLightTheme - val colors = remember(isLightTheme) { - if (isLightTheme) { - listOf( - LightColorTokens.colorBlue900, - LightColorTokens.colorGreen1100, - ) - } else { - listOf( - DarkColorTokens.colorBlue900, - DarkColorTokens.colorGreen1100, - ) + val colors = if (LocalBuildMeta.current.isEnterpriseBuild) { + listOf( + ElementTheme.colors.textActionAccent, + ElementTheme.colors.textActionAccent, + ) + } else { + remember(isLightTheme) { + if (isLightTheme) { + listOf( + LightColorTokens.colorBlue900, + LightColorTokens.colorGreen1100, + ) + } else { + listOf( + DarkColorTokens.colorBlue900, + DarkColorTokens.colorGreen1100, + ) + } } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt index 651bcd5f5c..54d1ea738e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt @@ -10,10 +10,12 @@ package io.element.android.libraries.designsystem.components.list import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.LocalContentColor import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp @@ -77,8 +79,9 @@ sealed interface ListItemContent { /** * Default Icon content for [ListItem]. Sets the Icon component to a predefined size. * @param iconSource The icon to display, using [IconSource.getPainter]. + * @param tintColor The tint color for the icon, if any. Defaults to `null`. */ - data class Icon(val iconSource: IconSource) : ListItemContent + data class Icon(val iconSource: IconSource, val tintColor: Color? = null) : ListItemContent /** * Default Text content for [ListItem]. Sets the Text component to a max size and clips overflow. @@ -119,7 +122,8 @@ sealed interface ListItemContent { IconComponent( modifier = Modifier.size(maxCompactSize), painter = iconSource.getPainter(), - contentDescription = iconSource.contentDescription + contentDescription = iconSource.contentDescription, + tint = tintColor ?: LocalContentColor.current, ) } is Text -> TextComponent(modifier = Modifier.widthIn(max = 128.dp), text = text, maxLines = 1, overflow = TextOverflow.Ellipsis) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt index a1b921988a..ff98ab5b1e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt @@ -12,10 +12,7 @@ import io.element.android.libraries.designsystem.R // This list and all the drawable it contains should be removed at some point. // All the icons should be defined in Compound. internal val iconsOther = listOf( - R.drawable.ic_cancel, - R.drawable.ic_encryption_enabled, - R.drawable.ic_notification_small, - R.drawable.ic_plus_composer, + R.drawable.ic_notification, R.drawable.ic_stop, R.drawable.pin, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementThemeApp.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementThemeApp.kt index 0944664074..bb0fcd971b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementThemeApp.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementThemeApp.kt @@ -9,17 +9,40 @@ package io.element.android.libraries.designsystem.theme import androidx.appcompat.app.AppCompatDelegate import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.Theme import io.element.android.compound.theme.isDark import io.element.android.compound.theme.mapToTheme import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.core.meta.BuildMeta +import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.preferences.api.store.AppPreferencesStore +val LocalBuildMeta = staticCompositionLocalOf { + BuildMeta( + isDebuggable = true, + buildType = BuildType.DEBUG, + applicationName = "MyApp", + productionApplicationName = "MyAppProd", + desktopApplicationName = "MyAppDesktop", + applicationId = "AppId", + isEnterpriseBuild = false, + lowPrivacyLoggingEnabled = false, + versionName = "aVersion", + versionCode = 123, + gitRevision = "aRevision", + gitBranchName = "aBranch", + flavorDescription = "aFlavor", + flavorShortDescription = "aFlavorShort", + ) +} + /** * Theme to use for all the regular screens of the application. * Will manage the light / dark theme based on the user preference. @@ -31,6 +54,7 @@ import io.element.android.libraries.preferences.api.store.AppPreferencesStore fun ElementThemeApp( appPreferencesStore: AppPreferencesStore, enterpriseService: EnterpriseService, + buildMeta: BuildMeta, content: @Composable () -> Unit, ) { val theme by remember { @@ -48,10 +72,14 @@ fun ElementThemeApp( } val compoundLight = remember { enterpriseService.semanticColorsLight() } val compoundDark = remember { enterpriseService.semanticColorsDark() } - ElementTheme( - darkTheme = theme.isDark(), - content = content, - compoundLight = compoundLight, - compoundDark = compoundDark, - ) + CompositionLocalProvider( + LocalBuildMeta provides buildMeta, + ) { + ElementTheme( + darkTheme = theme.isDark(), + content = content, + compoundLight = compoundLight, + compoundDark = compoundDark, + ) + } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconColorButton.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconColorButton.kt new file mode 100644 index 0000000000..8e7407c6ac --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/IconColorButton.kt @@ -0,0 +1,111 @@ +/* + * 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.theme.components + +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.ui.strings.CommonStrings + +/** + * Button with colored background. + * Figma: https://www.figma.com/design/G1xy0HDZKJf5TCRFmKb5d5/Compound-Android-Components?node-id=1956-37586 + */ +@Composable +fun IconColorButton( + onClick: () -> Unit, + imageVector: ImageVector, + modifier: Modifier = Modifier, + buttonSize: ButtonSize = ButtonSize.Large, + iconColorButtonStyle: IconColorButtonStyle = IconColorButtonStyle.Primary, +) { + val bgColor = when (iconColorButtonStyle) { + IconColorButtonStyle.Primary -> ElementTheme.colors.iconPrimary + IconColorButtonStyle.Secondary -> ElementTheme.colors.iconSecondary + IconColorButtonStyle.Disabled -> ElementTheme.colors.iconDisabled + } + IconButton( + modifier = modifier.size(48.dp), + onClick = onClick, + ) { + Icon( + modifier = Modifier + .clip(CircleShape) + .size(buttonSize.toContainerSize()) + .background(bgColor) + .padding(buttonSize.toContainerPadding()), + imageVector = imageVector, + contentDescription = stringResource(CommonStrings.action_close), + tint = ElementTheme.colors.iconOnSolidPrimary + ) + } +} + +enum class IconColorButtonStyle { + Primary, + Secondary, + Disabled, +} + +private fun ButtonSize.toContainerSize() = when (this) { + ButtonSize.Small -> 20.dp + ButtonSize.Medium -> 24.dp + ButtonSize.Large, + ButtonSize.MediumLowPadding, + ButtonSize.LargeLowPadding -> 30.dp +} + +private fun ButtonSize.toContainerPadding() = when (this) { + ButtonSize.Small -> 2.dp + ButtonSize.Medium -> 2.dp + ButtonSize.Large, + ButtonSize.MediumLowPadding, + ButtonSize.LargeLowPadding -> 3.dp +} + +@PreviewsDayNight +@Composable +internal fun IconColorButtonPreview() = ElementPreview { + Column { + listOf( + IconColorButtonStyle.Primary, + IconColorButtonStyle.Secondary, + IconColorButtonStyle.Disabled, + ).forEach { style -> + Row( + modifier = Modifier.padding(4.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + listOf(ButtonSize.Large, ButtonSize.Medium, ButtonSize.Small).forEach { size -> + IconColorButton( + onClick = {}, + imageVector = CompoundIcons.Close(), + buttonSize = size, + iconColorButtonStyle = style, + ) + } + } + } + } +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/OpenUrlInTabView.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/OpenUrlInTabView.kt new file mode 100644 index 0000000000..9770c6849d --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/utils/OpenUrlInTabView.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.libraries.designsystem.utils + +import androidx.activity.compose.LocalActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import io.element.android.compound.theme.ElementTheme +import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab + +@Suppress("MutableStateParam") +@Composable +fun OpenUrlInTabView(url: MutableState) { + val activity = requireNotNull(LocalActivity.current) + val darkTheme = ElementTheme.isLightTheme.not() + + LaunchedEffect(url.value) { + url.value?.let { + activity.openUrlInChromeCustomTab(null, darkTheme, it) + url.value = null + } + } +} diff --git a/libraries/designsystem/src/main/res/drawable/ic_cancel.xml b/libraries/designsystem/src/main/res/drawable/ic_cancel.xml deleted file mode 100644 index c36efcec94..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_cancel.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml b/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml deleted file mode 100644 index c29636b311..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_notification_small.xml b/libraries/designsystem/src/main/res/drawable/ic_notification.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_notification_small.xml rename to libraries/designsystem/src/main/res/drawable/ic_notification.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_plus_composer.xml b/libraries/designsystem/src/main/res/drawable/ic_plus_composer.xml deleted file mode 100644 index e22f124645..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_plus_composer.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/libraries/encrypted-db/build.gradle.kts b/libraries/encrypted-db/build.gradle.kts index 7e7b5bab84..ea53646cfc 100644 --- a/libraries/encrypted-db/build.gradle.kts +++ b/libraries/encrypted-db/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { implementation(libs.sqldelight.driver.android) implementation(libs.sqlcipher) implementation(libs.sqlite) - implementation(libs.androidx.security.crypto) + implementation(libs.google.tink) implementation(projects.libraries.androidutils) } diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFile.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFile.kt new file mode 100644 index 0000000000..9e9f6e1b2f --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFile.kt @@ -0,0 +1,82 @@ +/* + * 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.encrypteddb.crypto + +import android.content.Context +import com.google.crypto.tink.KeyTemplates +import com.google.crypto.tink.RegistryConfiguration +import com.google.crypto.tink.StreamingAead +import com.google.crypto.tink.integration.android.AndroidKeysetManager +import com.google.crypto.tink.streamingaead.StreamingAeadConfig +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream + +/** + * This class is used to write and read encrypted data to/from a file. + * + * It's a simplified version of the same class in [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary). + * + * It uses hardcoded constants that are used in that library, for backwards compatibility reasons. + */ +internal class EncryptedFile( + private val context: Context, + private val file: File +) { + companion object { + /** + * The file content is encrypted using StreamingAead with AES-GCM, with the file name as associated data. + */ + private const val KEYSET_ENCRYPTION_SCHEME = "AES256_GCM_HKDF_4KB" + + /** + * The keyset is stored in a shared preference file with this name. + */ + private const val KEYSET_PREF_FILE_NAME = "__androidx_security_crypto_encrypted_file_pref__" + + /** + * The keyset is stored in a shared preference with this key, inside the specified file. + */ + private const val KEYSET_ALIAS = "__androidx_security_crypto_encrypted_file_keyset__" + + /** + * The URI referencing the master key in the Android Keystore used to encrypt/decrypt the keyset. + */ + private const val MASTER_KEY_URI = "android-keystore://_androidx_security_master_key_" + } + + private val androidKeysetManager by lazy { + val keysetManagerBuilder = AndroidKeysetManager.Builder() + .withKeyTemplate(KeyTemplates.get(KEYSET_ENCRYPTION_SCHEME)) + .withSharedPref(context, KEYSET_ALIAS, KEYSET_PREF_FILE_NAME) + .withMasterKeyUri(MASTER_KEY_URI) + + keysetManagerBuilder.build() + } + + private val streamingAead: StreamingAead by lazy { + val streamingAeadKeysetHandle = androidKeysetManager.keysetHandle + streamingAeadKeysetHandle.getPrimitive(RegistryConfiguration.get(), StreamingAead::class.java) + } + + init { + StreamingAeadConfig.register() + } + + fun openFileOutput(): FileOutputStream { + val fos = FileOutputStream(file) + val stream = streamingAead.newEncryptingStream(fos, file.name.toByteArray()) + return EncryptedFileOutputStream(fos.fd, stream) + } + + fun openFileInput(): FileInputStream { + val fis = FileInputStream(file) + val stream = streamingAead.newDecryptingStream(fis, file.name.toByteArray()) + return EncryptedFileInputStream(fis.fd, stream) + } +} diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileInputStream.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileInputStream.kt new file mode 100644 index 0000000000..8ef15f3be5 --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileInputStream.kt @@ -0,0 +1,53 @@ +/* + * 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.encrypteddb.crypto + +import android.os.Build +import androidx.annotation.RequiresApi +import java.io.FileDescriptor +import java.io.FileInputStream +import java.io.InputStream + +/** + * This class is used to read encrypted data from a file. + * + * It comes directly from [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary). + */ +internal class EncryptedFileInputStream( + fileDescriptor: FileDescriptor, + private val inputStream: InputStream, +) : FileInputStream(fileDescriptor) { + private val lock = Any() + + override fun read(): Int = inputStream.read() + + override fun read(b: ByteArray?): Int = inputStream.read(b) + + override fun read(b: ByteArray?, off: Int, len: Int): Int = inputStream.read(b, off, len) + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun readAllBytes(): ByteArray? = inputStream.readAllBytes() + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun readNBytes(b: ByteArray?, off: Int, len: Int): Int = inputStream.readNBytes(b, off, len) + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + override fun readNBytes(len: Int): ByteArray? = inputStream.readNBytes(len) + + override fun skip(n: Long): Long = inputStream.skip(n) + + override fun available(): Int = inputStream.available() + + override fun mark(readlimit: Int) = synchronized(lock) { inputStream.mark(readlimit) } + + override fun markSupported(): Boolean = inputStream.markSupported() + + override fun reset() = synchronized(lock) { inputStream.reset() } + + override fun close() = inputStream.close() +} diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileOutputStream.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileOutputStream.kt new file mode 100644 index 0000000000..539ca0f490 --- /dev/null +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/crypto/EncryptedFileOutputStream.kt @@ -0,0 +1,32 @@ +/* + * 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.encrypteddb.crypto + +import java.io.FileDescriptor +import java.io.FileOutputStream +import java.io.OutputStream + +/** + * This class is used to write encrypted data to a file. + * + * It comes directly from [androidx.security.crypto](https://developer.android.com/reference/androidx/security/crypto/package-summary). + */ +internal class EncryptedFileOutputStream( + fileDescriptor: FileDescriptor, + private val outputStream: OutputStream +) : FileOutputStream(fileDescriptor) { + override fun write(b: ByteArray?) = outputStream.write(b) + + override fun write(b: ByteArray?, off: Int, len: Int) = outputStream.write(b, off, len) + + override fun write(b: Int) = outputStream.write(b) + + override fun flush() = outputStream.flush() + + override fun close() = outputStream.close() +} diff --git a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt index 39bd683f27..601dce508a 100644 --- a/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt +++ b/libraries/encrypted-db/src/main/kotlin/io/element/encrypteddb/passphrase/RandomSecretPassphraseProvider.kt @@ -8,8 +8,7 @@ package io.element.encrypteddb.passphrase import android.content.Context -import androidx.security.crypto.EncryptedFile -import io.element.android.libraries.androidutils.file.EncryptedFileFactory +import io.element.encrypteddb.crypto.EncryptedFile import java.io.File import java.security.SecureRandom @@ -25,7 +24,7 @@ class RandomSecretPassphraseProvider( private val secretSize: Int = 256, ) : PassphraseProvider { override fun getPassphrase(): ByteArray { - val encryptedFile = EncryptedFileFactory(context).create(file) + val encryptedFile = EncryptedFile(context, file) return if (!file.exists()) { val secret = generateSecret() encryptedFile.openFileOutput().use { it.write(secret) } diff --git a/libraries/eventformatter/impl/src/main/res/values-es/translations.xml b/libraries/eventformatter/impl/src/main/res/values-es/translations.xml index 33ed87e423..65f16e35fe 100644 --- a/libraries/eventformatter/impl/src/main/res/values-es/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-es/translations.xml @@ -30,7 +30,7 @@ "Te uniste a la sala" "%1$s solicitó unirse" "%1$s permitió que %2$s se uniera" - "%1$s te permitió unirte" + "Permitiste que %1$s se uniera" "Solicitaste unirte" "%1$s rechazó la solicitud de %2$s para unirse" "Rechazaste la solicitud de %1$s para unirte" @@ -45,6 +45,12 @@ "Eliminaste el nombre de la sala" "%1$s no hizo cambios" "No has hecho ningún cambio" + "%1$s cambió los mensajes fijados" + "Has cambiado los mensajes fijados" + "%1$s fijó un mensaje" + "Has fijado un mensaje" + "%1$s desprendió un mensaje" + "Desprendiste un mensaje" "%1$s rechazó la invitación" "Rechazaste la invitación" "%1$s echó a %2$s" diff --git a/libraries/eventformatter/impl/src/main/res/values-eu/translations.xml b/libraries/eventformatter/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..ecd1489c3d --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,65 @@ + + + "(abatarra ere aldatu da)" + "%1$s(e)k abatarra aldatu du" + "Abatarra aldatu duzu" + "%1$s kide mailara jaitsi da" + "%1$s moderatzaile mailara jaitsi da" + "%1$s(e)k pantaila-izena %2$s(e)tik %3$s(e)ra aldatu du" + "Pantaila-izena %1$s(e)tik %2$s(e)ra aldatu duzu" + "%1$s(e)k pantaila-izena kendu du (%2$s zen)" + "Pantaila-izena kendu duzu (%1$s zen)" + "%1$s(e)k pantaila-izena %2$s(r)a aldatu du" + "Pantaila-izena %1$s(e)ra aldatu duzu" + "%1$s administratzaile mailara igo da" + "%1$s moderatzaile mailara igo da" + "%1$s(e)k gelako abatarra aldatu du" + "Gelako abatarra aldatu duzu" + "%1$s(e)k gelaren abatarra kendu du" + "Gelaren abatarra kendu duzu" + "%1$s(e)k %2$s(r) debekua ezarri dio" + "%1$s(r) debekua ezarri diozu" + "%1$s(e)k gela sortu du" + "Gela sortu duzu" + "%1$s(e)k %2$s gonbidatu du" + "%1$s(e)k gonbidapena onartu du" + "Gonbidapena onartu duzu" + "%1$s gonbidatu duzu" + "%1$s(e)k gonbidatu zaitu" + "%1$s gelara batu da" + "Gelara batu zara" + "%1$s(e)k batzeko eskaera egin du" + "%1$s(e)k batzeko baimena eman dio %2$s(r)i" + "Batzeko baimena eman diozu %1$s(r)i" + "Batzeko eskaera egin duzu" + "%1$s(e)k %2$s(r)en bat egiteko eskaera baztertu du" + "%1$s(r)en bat egiteko eskaera baztertu duzu" + "%1$s(e)k bat egiteko eskaera baztertu dizu" + "%1$s(e)k dagoeneko ez du bat egiteko interesik" + "Bat egiteko eskaera bertan behera utzi duzu" + "%1$s gelatik atera da" + "Gelatik atera zara" + "%1$s(e)k gelaren izena honakora aldatu du: %2$s" + "Gelaren izena honakora aldatu duzu: %1$s" + "%1$s(e)k gelaren izena kendu du" + "Gelaren izena kendu duzu" + "%1$s(e)k ez du aldaketarik egin" + "Ez duzu aldaketarik egin" + "%1$s(e)k finkatutako mezuak aldatu ditu" + "Finkatutako mezuak aldatu dituzu" + "%1$s(e)k mezu bat finkatu du" + "Mezu bat finkatu duzu" + "%1$s(e)k gonbidapena baztertu du" + "Gonbidapena baztertu duzu" + "%1$s(e)k %2$s kendu du" + "%1$s kendu duzu" + "%1$s(e)k %2$s(r)i gonbidapena bidali dio gelara batu dadin" + "Gonbidapena bidali diozu %1$s(r)i gelara batu dadin" + "%1$s(e)k gaia honakora aldatu du: %2$s" + "Gaia honakora aldatu duzu: %1$s" + "%1$s(e)k gelaren gaia aldatu du" + "Gelaren gaia aldatu duzu" + "%1$s(e)k %2$s(r)en debekua bertan behera utzi du" + "%1$s(r)en debekua bertan behera utzi duzu" + "%1$s(e)k kidetzan aldaketa ezezagun bat egin du" + diff --git a/libraries/eventformatter/impl/src/main/res/values-ka/translations.xml b/libraries/eventformatter/impl/src/main/res/values-ka/translations.xml index 7fa4f65716..f60914be43 100644 --- a/libraries/eventformatter/impl/src/main/res/values-ka/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-ka/translations.xml @@ -3,12 +3,16 @@ "(ფოტოც შეიცვალა)" "%1$s პროფილის ფოტო შეცვალა" "თქვენ შეცვალეთ პროფილის ფოტო" + "%1$s დაქვეითდა წევრამდე" + "%1$s დაქვეითდა მოდერატორამდე" "%1$s თავისი ნაჩვენები სახელი შეცვალა %2$s დან %3$s ზე" "თქვენ შეცვალეთ თქვენი ნაჩვენები სახელი %1$s -დან %2$s -ზე" "%1$s წაშალა თავისი ნაჩვენები სახელი (იყო %2$s)" "თქვენ წაშალეთ ნაჩვენები სახელი (იყო %1$s)" "%1$s თავისი ნაჩვენები სახელი შეცვალა %2$s" "თქვენი ახალი ნაჩვენები სახელი - %1$s" + "%1$s დაწინაურდა ადმინისტრატორამდე" + "%1$s დაწინაურდა მოდერატორამდე" "%1$s ოთახის ფოტო შეცვალა" "თქვენ შეცვალეთ ოთახის ფოტო" "%1$s წაშალა ოთახის ფოტო" @@ -39,6 +43,8 @@ "თქვენ შეცვალეთ ოთახის სახელი: %1$s" "%1$s წაშალა ოთახის სახელი" "თქვენ წაშალეთ ოთახის სახელი" + "%1$s ცვლილებები არ შეიტანა" + "თქვენ არაფერი არ შეგიცვლიათ" "%1$s მოწვევაზე უარი თქვა" "თქვენ უარი თქვით მოწვევაზე" "%1$s გააგდო %2$s" diff --git a/libraries/eventformatter/impl/src/main/res/values-nb/translations.xml b/libraries/eventformatter/impl/src/main/res/values-nb/translations.xml index 6343ef8933..dfe9ada932 100644 --- a/libraries/eventformatter/impl/src/main/res/values-nb/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-nb/translations.xml @@ -43,6 +43,14 @@ "Du endret romnavnet til: %1$s" "%1$s fjernet romnavnet" "Du fjernet romnavnet" + "%1$s gjorde ingen endringer" + "Du har ikke gjort noen endringer" + "%1$s endret de festede meldingene" + "Du endret de festede meldingene" + "%1$s festet en melding" + "Du festet en melding" + "%1$s løsnet en melding" + "Du løsnet en melding" "%1$s avviste invitasjonen" "Du avviste invitasjonen" "%1$s fjernet %2$s" 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 9a683d49d9..f8e0d3405a 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 @@ -168,4 +168,14 @@ enum class FeatureFlags( defaultValue = { true }, isFinished = false, ), + PrintLogsToLogcat( + key = "feature.print_logs_to_logcat", + title = "Print logs to logcat", + description = "Print logs to logcat in addition to log files. Requires an app restart to take effect." + + "\n\nWARNING: this will make the logs visible in the device logs and may affect performance. " + + "It's not intended for daily usage in release builds.", + defaultValue = { buildMeta -> buildMeta.buildType != BuildType.RELEASE }, + // False so it's displayed in the developer options screen + isFinished = false, + ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt index ebded7fbe9..c221b2881e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/analytics/ViewRoomExt.kt @@ -18,8 +18,8 @@ fun MatrixRoom.toAnalyticsViewRoom( val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home return ViewRoom( - isDM = isDirect, - isSpace = isSpace, + isDM = info().isDirect, + isSpace = info().isSpace, trigger = trigger, activeSpace = activeSpace, viaKeyboard = viaKeyboard @@ -27,5 +27,5 @@ fun MatrixRoom.toAnalyticsViewRoom( } private fun MatrixRoom.toActiveSpace(): ViewRoom.ActiveSpace { - return if (isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private + return if (info().isPublic) ViewRoom.ActiveSpace.Public else ViewRoom.ActiveSpace.Private } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt index 86c9e4e825..79576e0adb 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcPrompt.kt @@ -8,34 +8,12 @@ package io.element.android.libraries.matrix.api.auth sealed interface OidcPrompt { - /** - * The Authorization Server must not display any authentication or consent - * user interface pages. - */ - data object None : OidcPrompt - /** * The Authorization Server should prompt the End-User for * reauthentication. */ data object Login : OidcPrompt - /** - * The Authorization Server should prompt the End-User for consent before - * returning information to the Client. - */ - data object Consent : OidcPrompt - - /** - * The Authorization Server should prompt the End-User to select a user - * account. - * - * This enables an End-User who has multiple accounts at the Authorization - * Server to select amongst the multiple accounts that they might have - * current sessions for. - */ - data object SelectAccount : OidcPrompt - /** * The Authorization Server should prompt the End-User to create a user * account. 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 a1a4eb9b1c..1635cce375 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 @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.encryption import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -75,6 +76,11 @@ interface EncryptionService { * user trust from verified to TOFU verified. */ suspend fun withdrawVerification(userId: UserId): Result + + /** + * Get the identity state of a user, if known. + */ + suspend fun getUserIdentity(userId: UserId): Result } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/platform/InitPlatformService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/platform/InitPlatformService.kt new file mode 100644 index 0000000000..6199d1704d --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/platform/InitPlatformService.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.libraries.matrix.api.platform + +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration + +/** + * This service is responsible for initializing the platform-related settings of the SDK. + */ +interface InitPlatformService { + /** + * Initialize the platform-related settings of the SDK. + * @param tracingConfiguration the tracing configuration to use for logging. + */ + fun init(tracingConfiguration: TracingConfiguration) +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 41b25c0dc0..ec94ec0b92 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -45,21 +45,10 @@ import java.io.File interface MatrixRoom : Closeable { val sessionId: SessionId val roomId: RoomId - val displayName: String - val canonicalAlias: RoomAlias? - val alternativeAliases: List - val topic: String? - val avatarUrl: String? - val isEncrypted: Boolean - val isSpace: Boolean - val isDirect: Boolean - val isPublic: Boolean - val activeMemberCount: Long - val joinedMemberCount: Long val roomCoroutineScope: CoroutineScope - val roomInfoFlow: Flow + val roomInfoFlow: StateFlow val roomTypingMembersFlow: Flow> val identityStateChangesFlow: Flow> @@ -72,7 +61,7 @@ interface MatrixRoom : Closeable { * A one-to-one is a room with exactly 2 members. * See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules). */ - val isOneToOne: Boolean get() = activeMemberCount == 2L + val isOneToOne: Boolean get() = info().activeMembersCount == 2L /** * The current loaded members as a StateFlow. @@ -83,6 +72,11 @@ interface MatrixRoom : Closeable { val roomNotificationSettingsStateFlow: StateFlow + /** + * Get the latest room info we have received from the SDK stream. + */ + fun info(): MatrixRoomInfo = roomInfoFlow.value + /** * Try to load the room members and update the membersFlow. */ @@ -453,4 +447,6 @@ interface MatrixRoom : Closeable { * Update the join rule for this room. */ suspend fun updateJoinRule(joinRule: JoinRule): Result + + suspend fun getUpdatedIsEncrypted(): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt index 32cc4646c0..ad526c6b40 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoomInfo.kt @@ -27,7 +27,9 @@ data class MatrixRoomInfo( val rawName: String?, val topic: String?, val avatarUrl: String?, + val isPublic: Boolean, val isDirect: Boolean, + val isEncrypted: Boolean?, val joinRule: JoinRule?, val isSpace: Boolean, val isTombstoned: Boolean, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt index b1c9787bfa..3dd54db9b1 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt @@ -7,6 +7,8 @@ package io.element.android.libraries.matrix.api.room +import kotlinx.coroutines.flow.first + /** * Returns whether the room with the provided info is a DM. * A DM is a room with at most 2 active members (one of them may have left). @@ -19,9 +21,9 @@ fun isDm(isDirect: Boolean, activeMembersCount: Int): Boolean { } /** - * Returns whether the [MatrixRoom] is a DM. + * Returns whether the [MatrixRoom] is a DM, with an updated state from the latest [MatrixRoomInfo]. */ -val MatrixRoom.isDm get() = isDm(isDirect, activeMemberCount.toInt()) +suspend fun MatrixRoom.isDm() = roomInfoFlow.first().isDm /** * Returns whether the [MatrixRoomInfo] is from a DM. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt index f9158ae360..25adbdf3be 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/alias/MatrixRoomAlias.kt @@ -19,7 +19,7 @@ fun MatrixRoom.matches(roomIdOrAlias: RoomIdOrAlias): Boolean { roomIdOrAlias.roomId == roomId } is RoomIdOrAlias.Alias -> { - roomIdOrAlias.roomAlias == canonicalAlias || roomIdOrAlias.roomAlias in alternativeAliases + roomIdOrAlias.roomAlias == info().canonicalAlias || roomIdOrAlias.roomAlias in info().alternativeAliases } } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt index 3e1d77fd3c..973b5cca0d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt @@ -32,7 +32,7 @@ suspend fun MatrixClient.getRecentDirectRooms( getRecentlyVisitedRooms().getOrNull()?.let { roomIds -> roomIds .mapNotNull { roomId -> getRoom(roomId) } - .filter { it.isDm && it.isJoined() } + .filter { it.isDm() && it.isJoined() } .map { room -> val otherUser = room.getMembers().getOrNull() ?.firstOrNull { it.userId != sessionId } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineItemEventOrigin.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineItemEventOrigin.kt index 10835f0448..528e28d5cf 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineItemEventOrigin.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/TimelineItemEventOrigin.kt @@ -10,5 +10,6 @@ package io.element.android.libraries.matrix.api.timeline.item.event enum class TimelineItemEventOrigin { LOCAL, SYNC, - PAGINATION + PAGINATION, + CACHE, } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt index 53ee37ca78..5cef6cde02 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/tracing/TracingService.kt @@ -10,6 +10,5 @@ package io.element.android.libraries.matrix.api.tracing import timber.log.Timber interface TracingService { - fun setupTracing(tracingConfiguration: TracingConfiguration) fun createTimberTree(target: String): Timber.Tree } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt index 7f48977ae9..7d460dc042 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationData.kt @@ -25,6 +25,4 @@ sealed interface SessionVerificationData { // https://spec.matrix.org/unstable/client-server-api/#sas-method-emoji data class VerificationEmoji( val number: Int, - val emoji: String, - val description: String, ) 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 aa4a7a4b8a..897e17c611 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 @@ -15,9 +15,15 @@ import kotlinx.parcelize.Parcelize @Parcelize data class SessionVerificationRequestDetails( - val senderId: UserId, + val senderProfile: SenderProfile, val flowId: FlowId, val deviceId: DeviceId, - val displayName: String?, val firstSeenTimestamp: Long, -) : Parcelable +) : Parcelable { + @Parcelize + data class SenderProfile( + val userId: UserId, + val displayName: String?, + val avatarUrl: String?, + ) : Parcelable +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt index cf9468b493..6e34c3de32 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt @@ -8,6 +8,7 @@ package io.element.android.libraries.matrix.api.verification import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -31,7 +32,12 @@ interface SessionVerificationService { /** * Request verification of the current session. */ - suspend fun requestVerification() + suspend fun requestCurrentSessionVerification() + + /** + * Request verification of the user with the given [userId]. + */ + suspend fun requestUserVerification(userId: UserId) /** * Cancels the current verification attempt. @@ -67,7 +73,7 @@ interface SessionVerificationService { * Set this particular request as the currently active one and register for * events pertaining it. */ - suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) + suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) /** * Accept the previously acknowledged verification request. @@ -76,7 +82,7 @@ interface SessionVerificationService { } interface SessionVerificationServiceListener { - fun onIncomingSessionRequest(sessionVerificationRequestDetails: SessionVerificationRequestDetails) + fun onIncomingSessionRequest(verificationRequest: VerificationRequest.Incoming) } /** Verification status of the current session. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationRequest.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationRequest.kt new file mode 100644 index 0000000000..f26afe50f6 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/VerificationRequest.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.verification + +import android.os.Parcelable +import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.parcelize.Parcelize + +sealed interface VerificationRequest : Parcelable { + sealed interface Outgoing : VerificationRequest { + @Parcelize + data object CurrentSession : Outgoing + + @Parcelize + data class User(val userId: UserId) : Outgoing + } + + sealed class Incoming(open val details: SessionVerificationRequestDetails) : VerificationRequest { + @Parcelize + data class OtherSession(override val details: SessionVerificationRequestDetails) : Incoming(details) + + @Parcelize + data class User(override val details: SessionVerificationRequestDetails) : Incoming(details) + } +} diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 90a76932c9..790d23ec07 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) - implementation("net.java.dev.jna:jna:5.16.0@aar") + implementation("net.java.dev.jna:jna:5.17.0@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 1f08be27b3..c2b20e1786 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 @@ -8,7 +8,6 @@ package io.element.android.libraries.matrix.impl import io.element.android.libraries.androidutils.file.getSizeOfFiles -import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope @@ -151,8 +150,7 @@ class RustMatrixClient( private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(innerSyncService) private val innerNotificationClient = runBlocking { innerClient.notificationClient(notificationProcessSetup) } private val notificationService = RustNotificationService(innerNotificationClient, dispatchers, clock) - private val notificationSettingsService = RustNotificationSettingsService(innerClient, dispatchers) - .apply { start() } + private val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers) private val encryptionService = RustEncryptionService( client = innerClient, syncService = rustSyncService, @@ -216,7 +214,7 @@ class RustMatrixClient( userId = sessionId, // TODO cache for displayName? displayName = null, - avatarUrl = innerClient.cachedAvatarUrl(), + avatarUrl = null, ) ) @@ -237,6 +235,9 @@ class RustMatrixClient( sessionDelegate.bindClient(this) sessionCoroutineScope.launch { + // Start notification settings + notificationSettingsService.start() + // Force a refresh of the profile getUserProfile() } @@ -479,18 +480,18 @@ class RustMatrixClient( appCoroutineScope.launch { roomFactory.destroy() rustSyncService.destroy() + notificationSettingsService.destroy() } sessionCoroutineScope.cancel() clientDelegateTaskHandle?.cancelAndDestroy() - notificationSettingsService.destroy() verificationService.destroy() sessionDelegate.clearCurrentClient() - innerRoomListService.destroy() - notificationService.destroy() + innerRoomListService.close() + notificationService.close() notificationProcessSetup.destroy() - encryptionService.destroy() - innerClient.destroy() + encryptionService.close() + innerClient.close() } override suspend fun getCacheSize(): Long { @@ -498,8 +499,8 @@ class RustMatrixClient( } override suspend fun clearCache() { + innerClient.clearCaches() close() - deleteSessionDirectory(deleteCryptoDb = false) } override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) { @@ -524,7 +525,7 @@ class RustMatrixClient( } close() - deleteSessionDirectory(deleteCryptoDb = true) + deleteSessionDirectory() if (userInitiated) { sessionStore.removeSession(sessionId.value) } @@ -573,7 +574,7 @@ class RustMatrixClient( } } close() - deleteSessionDirectory(deleteCryptoDb = true) + deleteSessionDirectory() sessionStore.removeSession(sessionId.value) }.onFailure { Timber.e(it, "Failed to deactivate account") @@ -661,25 +662,9 @@ class RustMatrixClient( } } - private suspend fun deleteSessionDirectory( - deleteCryptoDb: Boolean = false, - ): Boolean = withContext(sessionDispatcher) { - val sessionPaths = sessionPathsProvider.provides(sessionId) ?: return@withContext false - // Always delete the cache directory - sessionPaths.cacheDirectory.deleteRecursively() - if (deleteCryptoDb) { - // Delete the folder and all its content - sessionPaths.fileDirectory.deleteRecursively() - } else { - // Do not delete the crypto database files. - sessionPaths.fileDirectory.listFiles().orEmpty() - .filterNot { it.name.contains("matrix-sdk-crypto") } - .forEach { file -> - Timber.w("Deleting file ${file.name}...") - file.safeDelete() - } - true - } + private suspend fun deleteSessionDirectory() = withContext(sessionDispatcher) { + // Delete all the files for this session + sessionPathsProvider.provides(sessionId)?.deleteRecursively() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt index f98f9fedc8..1e76e69fa4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt @@ -9,6 +9,8 @@ package io.element.android.libraries.matrix.impl.analytics import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.isDm +import kotlinx.coroutines.flow.first private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize { return when (this) { @@ -22,11 +24,12 @@ private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize { } } -fun MatrixRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom { +suspend fun MatrixRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom { + val roomInfo = roomInfoFlow.first() return JoinedRoom( - isDM = isDirect, - isSpace = isSpace, - roomSize = joinedMemberCount.toAnalyticsRoomSize(), + isDM = roomInfo.isDm, + isSpace = roomInfo.isSpace, + roomSize = roomInfo.joinedMembersCount.toAnalyticsRoomSize(), trigger = trigger ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt index ea1eb93b29..0ba9eb4c49 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcPrompt.kt @@ -12,10 +12,7 @@ import org.matrix.rustcomponents.sdk.OidcPrompt as RustOidcPrompt internal fun OidcPrompt.toRustPrompt(): RustOidcPrompt { return when (this) { - OidcPrompt.None -> RustOidcPrompt.None - OidcPrompt.Login -> RustOidcPrompt.Login - OidcPrompt.Consent -> RustOidcPrompt.Consent - OidcPrompt.SelectAccount -> RustOidcPrompt.SelectAccount + OidcPrompt.Login -> RustOidcPrompt.Unknown("consent") OidcPrompt.Create -> RustOidcPrompt.Create is OidcPrompt.Unknown -> RustOidcPrompt.Unknown(value) } 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 725627104c..208f684fa1 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 @@ -48,7 +48,7 @@ import org.matrix.rustcomponents.sdk.QrCodeDecodeException import org.matrix.rustcomponents.sdk.QrLoginProgress import org.matrix.rustcomponents.sdk.QrLoginProgressListener import timber.log.Timber -import uniffi.matrix_sdk.OidcAuthorizationData +import uniffi.matrix_sdk.OAuthAuthorizationData import javax.inject.Inject @ContributesBinding(AppScope::class) @@ -183,18 +183,18 @@ class RustMatrixAuthenticationService @Inject constructor( } } - private var pendingOidcAuthorizationData: OidcAuthorizationData? = null + private var pendingOAuthAuthorizationData: OAuthAuthorizationData? = null override suspend fun getOidcUrl(prompt: OidcPrompt): Result { return withContext(coroutineDispatchers.io) { runCatching { val client = currentClient ?: error("You need to call `setHomeserver()` first") - val oidcAuthenticationData = client.urlForOidc( + val oAuthAuthenticationData = client.urlForOidc( oidcConfiguration = oidcConfigurationProvider.get(), prompt = prompt.toRustPrompt(), ) - val url = oidcAuthenticationData.loginUrl() - pendingOidcAuthorizationData = oidcAuthenticationData + val url = oAuthAuthenticationData.loginUrl() + pendingOAuthAuthorizationData = oAuthAuthenticationData OidcDetails(url) }.mapFailure { failure -> failure.mapAuthenticationException() @@ -205,8 +205,8 @@ class RustMatrixAuthenticationService @Inject constructor( override suspend fun cancelOidcLogin(): Result { return withContext(coroutineDispatchers.io) { runCatching { - pendingOidcAuthorizationData?.close() - pendingOidcAuthorizationData = null + pendingOAuthAuthorizationData?.close() + pendingOAuthAuthorizationData = null }.mapFailure { failure -> failure.mapAuthenticationException() } @@ -221,7 +221,7 @@ class RustMatrixAuthenticationService @Inject constructor( runCatching { val client = currentClient ?: error("You need to call `setHomeserver()` first") val currentSessionPaths = sessionPaths ?: error("You need to call `setHomeserver()` first") - val urlForOidcLogin = pendingOidcAuthorizationData ?: error("You need to call `getOidcUrl()` first") + val urlForOidcLogin = pendingOAuthAuthorizationData ?: error("You need to call `getOidcUrl()` first") client.loginWithOidcCallback(urlForOidcLogin, callbackUrl) val sessionData = client.session().toSessionData( isTokenValid = true, @@ -229,8 +229,8 @@ class RustMatrixAuthenticationService @Inject constructor( passphrase = pendingPassphrase, sessionPaths = currentSessionPaths, ) - pendingOidcAuthorizationData?.close() - pendingOidcAuthorizationData = null + pendingOAuthAuthorizationData?.close() + pendingOAuthAuthorizationData = null newMatrixClientObserver?.invoke(rustMatrixClientFactory.create(client)) sessionStore.storeData(sessionData) 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 7864c8c94f..0ffe5208f7 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 @@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress 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.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.sync.RustSyncService import kotlinx.coroutines.CoroutineScope @@ -202,25 +203,36 @@ internal class RustEncryptionService( } override suspend fun isUserVerified(userId: UserId): Result = runCatching { - getUserIdentity(userId).isVerified() + getUserIdentityInternal(userId).isVerified() } override suspend fun pinUserIdentity(userId: UserId): Result = runCatching { - getUserIdentity(userId).pin() + getUserIdentityInternal(userId).pin() } override suspend fun withdrawVerification(userId: UserId): Result = runCatching { - getUserIdentity(userId).withdrawVerification() + getUserIdentityInternal(userId).withdrawVerification() } - private suspend fun getUserIdentity(userId: UserId): UserIdentity { + override suspend fun getUserIdentity(userId: UserId): Result = runCatching { + val identity = getUserIdentityInternal(userId) + val isVerified = identity.isVerified() + when { + identity.hasVerificationViolation() -> IdentityState.VerificationViolation + isVerified -> IdentityState.Verified + !isVerified -> IdentityState.Pinned + else -> null + } + } + + suspend fun getUserIdentityInternal(userId: UserId): UserIdentity { return service.userIdentity( userId = userId.value, // requestFromHomeserverIfNeeded = true, ) ?: error("User identity not found") } - fun destroy() { - service.destroy() + fun close() { + service.close() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index 7b8e2f46d8..dfc891ace8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -36,7 +36,7 @@ class RustNotificationService( } } - fun destroy() { - notificationClient.destroy() + fun close() { + notificationClient.close() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt index b08a6675a4..5cfd9741dd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsService.kt @@ -8,10 +8,12 @@ package io.element.android.libraries.matrix.impl.notificationsettings import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.coroutine.suspendLazy import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.matrix.api.room.RoomNotificationSettings +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow @@ -24,9 +26,10 @@ import timber.log.Timber class RustNotificationSettingsService( client: Client, + sessionCoroutineScope: CoroutineScope, private val dispatchers: CoroutineDispatchers, ) : NotificationSettingsService { - private val notificationSettings = client.getNotificationSettings() + private val notificationSettings by suspendLazy(sessionCoroutineScope.coroutineContext + dispatchers.io) { client.getNotificationSettings() } private val _notificationSettingsChangeFlow = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) override val notificationSettingsChangeFlow: SharedFlow = _notificationSettingsChangeFlow.asSharedFlow() @@ -36,22 +39,22 @@ class RustNotificationSettingsService( } } - fun start() { - notificationSettings.setDelegate(notificationSettingsDelegate) + suspend fun start() { + notificationSettings.await().setDelegate(notificationSettingsDelegate) } - fun destroy() { - notificationSettings.setDelegate(null) + suspend fun destroy() { + notificationSettings.await().setDelegate(null) } override suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result = runCatching { - notificationSettings.getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map) + notificationSettings.await().getRoomNotificationSettings(roomId.value, isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::map) } override suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result = runCatching { - notificationSettings.getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode) + notificationSettings.await().getDefaultRoomNotificationMode(isEncrypted, isOneToOne).let(RoomNotificationSettingsMapper::mapMode) } override suspend fun setDefaultRoomNotificationMode( @@ -61,7 +64,7 @@ class RustNotificationSettingsService( ): Result = withContext(dispatchers.io) { runCatching { try { - notificationSettings.setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) + notificationSettings.await().setDefaultRoomNotificationMode(isEncrypted, isOneToOne, mode.let(RoomNotificationSettingsMapper::mapMode)) } catch (exception: NotificationSettingsException.RuleNotFound) { // `setDefaultRoomNotificationMode` updates multiple rules including unstable rules (e.g. the polls push rules defined in the MSC3930) // since production home servers may not have these rules yet, we drop the RuleNotFound error @@ -72,13 +75,13 @@ class RustNotificationSettingsService( override suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode)) + notificationSettings.await().setRoomNotificationMode(roomId.value, mode.let(RoomNotificationSettingsMapper::mapMode)) } } override suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.restoreDefaultRoomNotificationMode(roomId.value) + notificationSettings.await().restoreDefaultRoomNotificationMode(roomId.value) } } @@ -86,53 +89,53 @@ class RustNotificationSettingsService( override suspend fun unmuteRoom(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean) = withContext(dispatchers.io) { runCatching { - notificationSettings.unmuteRoom(roomId.value, isEncrypted, isOneToOne) + notificationSettings.await().unmuteRoom(roomId.value, isEncrypted, isOneToOne) } } override suspend fun isRoomMentionEnabled(): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.isRoomMentionEnabled() + notificationSettings.await().isRoomMentionEnabled() } } override suspend fun setRoomMentionEnabled(enabled: Boolean): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.setRoomMentionEnabled(enabled) + notificationSettings.await().setRoomMentionEnabled(enabled) } } override suspend fun isCallEnabled(): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.isCallEnabled() + notificationSettings.await().isCallEnabled() } } override suspend fun setCallEnabled(enabled: Boolean): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.setCallEnabled(enabled) + notificationSettings.await().setCallEnabled(enabled) } } override suspend fun isInviteForMeEnabled(): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.isInviteForMeEnabled() + notificationSettings.await().isInviteForMeEnabled() } } override suspend fun setInviteForMeEnabled(enabled: Boolean): Result = withContext(dispatchers.io) { runCatching { - notificationSettings.setInviteForMeEnabled(enabled) + notificationSettings.await().setInviteForMeEnabled(enabled) } } override suspend fun getRoomsWithUserDefinedRules(): Result> = runCatching { - notificationSettings.getRoomsWithUserDefinedRules(enabled = true) + notificationSettings.await().getRoomsWithUserDefinedRules(enabled = true) } override suspend fun canHomeServerPushEncryptedEventsToDevice(): Result = runCatching { - notificationSettings.canPushEncryptedEventToDevice() + notificationSettings.await().canPushEncryptedEventToDevice() } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/platform/RustInitPlatformService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/platform/RustInitPlatformService.kt new file mode 100644 index 0000000000..1324ebe796 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/platform/RustInitPlatformService.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.impl.platform + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.platform.InitPlatformService +import io.element.android.libraries.matrix.api.tracing.TracingConfiguration +import io.element.android.libraries.matrix.impl.tracing.map +import org.matrix.rustcomponents.sdk.initPlatform +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class RustInitPlatformService @Inject constructor() : InitPlatformService { + override fun init(tracingConfiguration: TracingConfiguration) { + initPlatform( + config = tracingConfiguration.map(), + useLightweightTokioRuntime = false + ) + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt index 5b39595e20..de72a54469 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapper.kt @@ -23,6 +23,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentMap import org.matrix.rustcomponents.sdk.Membership import org.matrix.rustcomponents.sdk.RoomHero +import uniffi.matrix_sdk_base.EncryptionState import org.matrix.rustcomponents.sdk.Membership as RustMembership import org.matrix.rustcomponents.sdk.RoomInfo as RustRoomInfo import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode @@ -36,7 +37,13 @@ class MatrixRoomInfoMapper { rawName = it.rawName, topic = it.topic, avatarUrl = it.avatarUrl, + isPublic = it.isPublic, isDirect = it.isDirect, + isEncrypted = when (it.encryptionState) { + EncryptionState.ENCRYPTED -> true + EncryptionState.NOT_ENCRYPTED -> false + EncryptionState.UNKNOWN -> null + }, joinRule = it.joinRule?.map(), isSpace = it.isSpace, isTombstoned = it.isTombstoned, @@ -63,6 +70,44 @@ class MatrixRoomInfoMapper { historyVisibility = it.historyVisibility.map(), ) } + +// fun map(rustRoom: Room): MatrixRoomInfo = with(rustRoom) { +// return MatrixRoomInfo( +// id = RoomId(id()), +// name = rawName(), +// rawName = displayName(), +// topic = topic(), +// avatarUrl = avatarUrl(), +// isPublic = isPublic(), +// isDirect = null, +// isEncrypted = encryptionState() == EncryptionState.ENCRYPTED, +// joinRule = null, +// isSpace = isSpace(), +// isTombstoned = isTombstoned(), +// isFavorite = null, +// canonicalAlias = canonicalAlias()?.let(::RoomAlias), +// alternativeAliases = alternativeAliases().map(::RoomAlias).toImmutableList(), +// currentUserMembership = membership().map(), +// inviter = null, +// activeMembersCount = activeMembersCount().toLong(), +// invitedMembersCount = invitedMembersCount().toLong(), +// joinedMembersCount = joinedMembersCount().toLong(), +// userPowerLevels = persistentMapOf(), +// highlightCount = 0, +// notificationCount = 0, +// userDefinedNotificationMode = null, +// hasRoomCall = hasActiveRoomCall(), +// activeRoomCallParticipants = activeRoomCallParticipants().map(::UserId).toImmutableList(), +// isMarkedUnread = false, +// numUnreadMessages = 0, +// numUnreadNotifications = 0, +// numUnreadMentions = 0, +// heroes = heroes().map(RoomHero::map).toImmutableList(), +// pinnedEventIds = persistentListOf(), +// creator = null, +// historyVisibility = null, +// ) +// } } fun RustMembership.map(): CurrentUserMembership = when (this) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index aac69e8844..5b742e83f8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -73,6 +73,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine @@ -82,13 +83,13 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.DateDividerMode import org.matrix.rustcomponents.sdk.IdentityStatusChangeListener import org.matrix.rustcomponents.sdk.KnockRequestsListener import org.matrix.rustcomponents.sdk.RoomInfo import org.matrix.rustcomponents.sdk.RoomInfoListener -import org.matrix.rustcomponents.sdk.RoomListItem import org.matrix.rustcomponents.sdk.RoomMessageEventMessageType import org.matrix.rustcomponents.sdk.TimelineConfiguration import org.matrix.rustcomponents.sdk.TimelineFilter @@ -101,6 +102,7 @@ import org.matrix.rustcomponents.sdk.getElementCallRequiredPermissions import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk.RoomPowerLevelChanges +import uniffi.matrix_sdk_base.EncryptionState import java.io.File import kotlin.coroutines.cancellation.CancellationException import org.matrix.rustcomponents.sdk.IdentityStatusChange as RustIdentityStateChange @@ -112,7 +114,6 @@ import org.matrix.rustcomponents.sdk.Timeline as InnerTimeline class RustMatrixRoom( override val sessionId: SessionId, private val deviceId: DeviceId, - private val roomListItem: RoomListItem, private val innerRoom: InnerRoom, innerTimeline: InnerTimeline, private val notificationSettingsService: NotificationSettingsService, @@ -124,22 +125,17 @@ class RustMatrixRoom( private val matrixRoomInfoMapper: MatrixRoomInfoMapper, private val featureFlagService: FeatureFlagService, private val roomMembershipObserver: RoomMembershipObserver, + initialRoomInfo: MatrixRoomInfo, ) : MatrixRoom { override val roomId = RoomId(innerRoom.id()) - override val roomInfoFlow: Flow = mxCallbackFlow { - runCatching { innerRoom.roomInfo() } - .getOrNull() - ?.let(matrixRoomInfoMapper::map) - ?.let { initial -> - channel.trySend(initial) - } + override val roomInfoFlow: StateFlow = mxCallbackFlow { innerRoom.subscribeToRoomInfoUpdates(object : RoomInfoListener { override fun call(roomInfo: RoomInfo) { channel.trySend(matrixRoomInfoMapper.map(roomInfo)) } }) - } + }.stateIn(sessionCoroutineScope, started = SharingStarted.Lazily, initialValue = initialRoomInfo) override val roomTypingMembersFlow: Flow> = mxCallbackFlow { val initial = emptyList() @@ -264,6 +260,9 @@ class RustMatrixRoom( CreateTimelineParams.PinnedOnly -> DateDividerMode.DAILY } + // Track read receipts only for focused timeline for performance optimization + val trackReadReceipts = createTimelineParams is CreateTimelineParams.Focused + runCatching { innerRoom.timelineWithConfiguration( configuration = TimelineConfiguration( @@ -271,7 +270,7 @@ class RustMatrixRoom( filter = filter, internalIdPrefix = internalIdPrefix, dateDividerMode = dateDividerMode, - trackReadReceipts = false, + trackReadReceipts = trackReadReceipts, ) ).let { inner -> val mode = when (createTimelineParams) { @@ -304,39 +303,6 @@ class RustMatrixRoom( liveTimeline.close() } - override val displayName: String - get() = runCatching { innerRoom.displayName() ?: "" }.getOrDefault("") - - override val topic: String? - get() = runCatching { innerRoom.topic() }.getOrDefault(null) - - override val avatarUrl: String? - get() = runCatching { roomListItem.avatarUrl() ?: innerRoom.avatarUrl() }.getOrDefault(null) - - override val isEncrypted: Boolean - get() = runCatching { innerRoom.isEncrypted() }.getOrDefault(false) - - override val canonicalAlias: RoomAlias? - get() = runCatching { innerRoom.canonicalAlias()?.let(::RoomAlias) }.getOrDefault(null) - - override val alternativeAliases: List - get() = runCatching { innerRoom.alternativeAliases().map { RoomAlias(it) } }.getOrDefault(emptyList()) - - override val isPublic: Boolean - get() = runCatching { innerRoom.isPublic() }.getOrDefault(false) - - override val isSpace: Boolean - get() = runCatching { innerRoom.isSpace() }.getOrDefault(false) - - override val isDirect: Boolean - get() = runCatching { innerRoom.isDirect() }.getOrDefault(false) - - override val joinedMemberCount: Long - get() = runCatching { innerRoom.joinedMembersCount().toLong() }.getOrDefault(0) - - override val activeMemberCount: Long - get() = runCatching { innerRoom.activeMembersCount().toLong() }.getOrDefault(0) - override suspend fun updateMembers() { val useCache = membersStateFlow.value is MatrixRoomMembersState.Unknown val source = if (useCache) { @@ -374,6 +340,7 @@ class RustMatrixRoom( val currentRoomNotificationSettings = currentState.roomNotificationSettings() _roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings) runCatching { + val isEncrypted = roomInfoFlow.value.isEncrypted ?: getUpdatedIsEncrypted().getOrThrow() notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow() }.map { _roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it) @@ -863,6 +830,12 @@ class RustMatrixRoom( } } + override suspend fun getUpdatedIsEncrypted(): Result = withContext(roomDispatcher) { + runCatching { + innerRoom.latestEncryptionState() == EncryptionState.ENCRYPTED + } + } + private fun createTimeline( timeline: InnerTimeline, mode: Timeline.Mode, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt index 4488b2c036..2bcd15c142 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt @@ -104,10 +104,10 @@ class RustRoomFactory( return@withContext null } val liveTimeline = roomReferences.fullRoom.timeline() + val initialRoomInfo = roomReferences.fullRoom.roomInfo() RustMatrixRoom( sessionId = sessionId, deviceId = deviceId, - roomListItem = roomReferences.roomListItem, innerRoom = roomReferences.fullRoom, innerTimeline = liveTimeline, sessionCoroutineScope = sessionCoroutineScope, @@ -119,6 +119,7 @@ class RustRoomFactory( matrixRoomInfoMapper = matrixRoomInfoMapper, featureFlagService = featureFlagService, roomMembershipObserver = roomMembershipObserver, + initialRoomInfo = matrixRoomInfoMapper.map(initialRoomInfo), ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 3bd3699037..46c2bfebd4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -56,6 +56,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.launchIn @@ -214,19 +215,19 @@ class RustTimeline( _timelineItems, backPaginationStatus, forwardPaginationStatus, - matrixRoom.roomInfoFlow.map { it.creator }, + matrixRoom.roomInfoFlow.map { it.creator to it.isDm }.distinctUntilChanged(), isTimelineInitialized, ) { timelineItems, backwardPaginationStatus, forwardPaginationStatus, - roomCreator, + (roomCreator, isDm), isTimelineInitialized -> withContext(dispatcher) { timelineItems .let { items -> roomBeginningPostProcessor.process( items = items, - isDm = matrixRoom.isDm, + isDm = isDm, roomCreator = roomCreator, hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad, ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index 3e2817bf9b..89661b5888 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -152,6 +152,7 @@ private fun RustEventItemOrigin.map(): TimelineItemEventOrigin { RustEventItemOrigin.LOCAL -> TimelineItemEventOrigin.LOCAL RustEventItemOrigin.SYNC -> TimelineItemEventOrigin.SYNC RustEventItemOrigin.PAGINATION -> TimelineItemEventOrigin.PAGINATION + RustEventItemOrigin.CACHE -> TimelineItemEventOrigin.CACHE } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt index 74fb2e9db1..0638e49974 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt @@ -20,17 +20,6 @@ import javax.inject.Inject @ContributesBinding(AppScope::class) class RustTracingService @Inject constructor(private val buildMeta: BuildMeta) : TracingService { - override fun setupTracing(tracingConfiguration: TracingConfiguration) { - val rustTracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( - writeToStdoutOrSystem = tracingConfiguration.writesToLogcat, - logLevel = tracingConfiguration.logLevel.toRustLogLevel(), - extraTargets = tracingConfiguration.extraTargets, - writeToFiles = tracingConfiguration.writesToFilesConfiguration.toTracingFileConfiguration(), - ) - org.matrix.rustcomponents.sdk.setupTracing(rustTracingConfiguration) - Timber.d("setupTracing: $rustTracingConfiguration") - } - override fun createTimberTree(target: String): Timber.Tree { return RustTracingTree(target = target, retrieveFromStackTrace = buildMeta.isDebuggable) } @@ -57,3 +46,10 @@ private fun WriteToFilesConfiguration.toTracingFileConfiguration(): TracingFileC ) } } + +fun TracingConfiguration.map(): org.matrix.rustcomponents.sdk.TracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( + writeToStdoutOrSystem = writesToLogcat, + logLevel = logLevel.toRustLogLevel(), + extraTargets = extraTargets, + writeToFiles = writesToFilesConfiguration.toTracingFileConfiguration(), +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt index 8cd9a47234..e714d511bf 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt @@ -8,13 +8,14 @@ package io.element.android.libraries.matrix.impl.verification import io.element.android.libraries.core.data.tryOrNull +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerificationData -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.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationFlowState +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.NonCancellable @@ -50,6 +51,8 @@ class RustSessionVerificationService( isSyncServiceReady: Flow, private val sessionCoroutineScope: CoroutineScope, ) : SessionVerificationService, SessionVerificationControllerDelegate { + private var currentVerificationRequest: VerificationRequest? = null + private val encryptionService: Encryption = client.encryption() private lateinit var verificationController: SessionVerificationController @@ -88,10 +91,8 @@ class RustSessionVerificationService( verificationStatus == SessionVerifiedStatus.NotVerified } - private var isOwnVerification = true - override fun didReceiveVerificationRequest(details: RustSessionVerificationRequestDetails) { - listener?.onIncomingSessionRequest(details.map()) + listener?.onIncomingSessionRequest(details.toVerificationRequest(UserId(client.userId()))) } private var listener: SessionVerificationServiceListener? = null @@ -111,9 +112,16 @@ class RustSessionVerificationService( this.listener = listener } - override suspend fun requestVerification() = tryOrFail { + override suspend fun requestCurrentSessionVerification() = tryOrFail { initVerificationControllerIfNeeded() verificationController.requestDeviceVerification() + currentVerificationRequest = VerificationRequest.Outgoing.CurrentSession + } + + override suspend fun requestUserVerification(userId: UserId) = tryOrFail { + initVerificationControllerIfNeeded() + verificationController.requestUserVerification(userId.value) + currentVerificationRequest = VerificationRequest.Outgoing.User(userId) } override suspend fun cancelVerification() = tryOrFail { @@ -130,16 +138,16 @@ class RustSessionVerificationService( verificationController.startSasVerification() } - override suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) = tryOrFail { - isOwnVerification = false + override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) = tryOrFail { initVerificationControllerIfNeeded() verificationController.acknowledgeVerificationRequest( - senderId = details.senderId.value, - flowId = details.flowId.value, + senderId = verificationRequest.details.senderProfile.userId.value, + flowId = verificationRequest.details.flowId.value, ) } override suspend fun acceptVerificationRequest() = tryOrFail { + Timber.d("Accepting incoming verification request") verificationController.acceptVerificationRequest() } @@ -183,7 +191,7 @@ class RustSessionVerificationService( } } .onSuccess { - if (isOwnVerification) { + if (currentVerificationRequest is VerificationRequest.Outgoing.CurrentSession) { // Try waiting for the final recovery state for better UX, but don't block the verification state on it tryOrNull { withTimeout(10.seconds) { @@ -215,7 +223,7 @@ class RustSessionVerificationService( // end-region override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) { - isOwnVerification = true + currentVerificationRequest = null if (isReady.value && cancelAnyPendingVerificationAttempt) { // Cancel any pending verification attempt tryOrNull { verificationController.cancelVerification() } @@ -266,8 +274,6 @@ private fun RustSessionVerificationData.map(): SessionVerificationData { emoji.use { sessionVerificationEmoji -> VerificationEmoji( number = sessionVerificationData.indices[index].toInt(), - emoji = sessionVerificationEmoji.symbol(), - description = sessionVerificationEmoji.description(), ) } }, 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 b617889f40..d2e15a3fac 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 @@ -11,12 +11,28 @@ 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.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.verification.VerificationRequest import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails +import org.matrix.rustcomponents.sdk.UserProfile as RustUserProfile fun RustSessionVerificationRequestDetails.map() = SessionVerificationRequestDetails( - senderId = UserId(senderProfile.userId), + senderProfile = senderProfile.map(), flowId = FlowId(flowId), deviceId = DeviceId(deviceId), - displayName = senderProfile.displayName, 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) { + VerificationRequest.Incoming.OtherSession(details) + } else { + VerificationRequest.Incoming.User(details) + } +} 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 c6fc62348e..887155e5d7 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 @@ -42,7 +42,7 @@ fun TestScope.createRustMatrixClientFactory( ) = RustMatrixClientFactory( baseDirectory = baseDirectory, cacheDirectory = cacheDirectory, - appCoroutineScope = this, + appCoroutineScope = backgroundScope, coroutineDispatchers = testCoroutineDispatchers(), sessionStore = sessionStore, userAgentProvider = SimpleUserAgentProvider(), 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 0af84fc6e3..33f943cf53 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 @@ -17,10 +17,12 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore import io.element.android.services.toolbox.test.systemclock.FakeSystemClock +import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test +import org.matrix.rustcomponents.sdk.Client import java.io.File class RustMatrixClientTest { @@ -32,13 +34,30 @@ class RustMatrixClientTest { } } + @Test + fun `clear cache invokes the method clearCaches from the client and close it`() = runTest { + val clearCachesResult = lambdaRecorder { } + val closeResult = lambdaRecorder { } + createRustMatrixClient( + client = FakeRustClient( + clearCachesResult = clearCachesResult, + closeResult = closeResult, + ) + ).use { sut -> + sut.clearCache() + clearCachesResult.assertions().isCalledOnce() + closeResult.assertions().isCalledOnce() + } + } + private fun TestScope.createRustMatrixClient( + client: Client = FakeRustClient(), sessionStore: SessionStore = InMemorySessionStore(), ) = RustMatrixClient( - innerClient = FakeRustClient(), + innerClient = client, baseDirectory = File(""), sessionStore = sessionStore, - appCoroutineScope = this, + appCoroutineScope = backgroundScope, sessionDelegate = aRustClientSessionDelegate( sessionStore = sessionStore, ), diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExtKtTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExtKtTest.kt index bce9097c5f..1ecfb672b8 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExtKtTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExtKtTest.kt @@ -9,13 +9,14 @@ package io.element.android.libraries.matrix.impl.analytics import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom -import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo +import kotlinx.coroutines.test.runTest import org.junit.Test class JoinedRoomExtKtTest { @Test - fun `test room size mapping`() { + fun `test room size mapping`() = runTest { mapOf( listOf(0L, 1L) to JoinedRoom.RoomSize.One, listOf(2L, 2L) to JoinedRoom.RoomSize.Two, @@ -39,7 +40,7 @@ class JoinedRoomExtKtTest { } @Test - fun `test isDirect parameter mapping`() { + fun `test isDirect parameter mapping`() = runTest { assertThat(aMatrixRoom(isDirect = true).toAnalyticsJoinedRoom(null)) .isEqualTo( JoinedRoom( @@ -52,7 +53,7 @@ class JoinedRoomExtKtTest { } @Test - fun `test isSpace parameter mapping`() { + fun `test isSpace parameter mapping`() = runTest { assertThat(aMatrixRoom(isSpace = true).toAnalyticsJoinedRoom(null)) .isEqualTo( JoinedRoom( @@ -65,8 +66,8 @@ class JoinedRoomExtKtTest { } @Test - fun `test trigger parameter mapping`() { - assertThat(aMatrixRoom().toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite)) + fun `test trigger parameter mapping`() = runTest { + assertThat(aMatrixRoom(isDirect = false, isSpace = false, joinedMemberCount = 1).toAnalyticsJoinedRoom(JoinedRoom.Trigger.Invite)) .isEqualTo( JoinedRoom( isDM = false, @@ -81,11 +82,9 @@ class JoinedRoomExtKtTest { isDirect: Boolean = false, isSpace: Boolean = false, joinedMemberCount: Long = 0 - ): MatrixRoom { - return FakeMatrixRoom( - isDirect = isDirect, - isSpace = isSpace, - joinedMemberCount = joinedMemberCount - ) + ): FakeMatrixRoom { + return FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo(isDirect = isDirect, isSpace = isSpace, joinedMembersCount = joinedMemberCount)) + } } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt index f5341a1c5e..15445183c0 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt @@ -17,6 +17,7 @@ import org.matrix.rustcomponents.sdk.RoomHistoryVisibility import org.matrix.rustcomponents.sdk.RoomInfo import org.matrix.rustcomponents.sdk.RoomMember import org.matrix.rustcomponents.sdk.RoomNotificationMode +import uniffi.matrix_sdk_base.EncryptionState fun aRustRoomInfo( id: String = A_ROOM_ID.value, @@ -24,6 +25,7 @@ fun aRustRoomInfo( rawName: String? = A_ROOM_NAME, topic: String? = null, avatarUrl: String? = null, + encryptionState: EncryptionState = EncryptionState.UNKNOWN, isDirect: Boolean = false, isPublic: Boolean = false, isSpace: Boolean = false, @@ -57,6 +59,7 @@ fun aRustRoomInfo( rawName = rawName, topic = topic, avatarUrl = avatarUrl, + encryptionState = encryptionState, isDirect = isDirect, isPublic = isPublic, isSpace = isSpace, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustClient.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustClient.kt index 0a9983d60f..6489396260 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustClient.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeRustClient.kt @@ -10,6 +10,8 @@ package io.element.android.libraries.matrix.impl.fixtures.fakes import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSession import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientDelegate import org.matrix.rustcomponents.sdk.Encryption @@ -31,15 +33,17 @@ class FakeRustClient( private val notificationSettings: NotificationSettings = FakeRustNotificationSettings(), private val encryption: Encryption = FakeRustEncryption(), private val session: Session = aRustSession(), + private val clearCachesResult: () -> Unit = { lambdaError() }, + private val closeResult: () -> Unit = {}, ) : Client(NoPointer) { override fun userId(): String = userId override fun deviceId(): String = deviceId override suspend fun notificationClient(processSetup: NotificationProcessSetup) = notificationClient - override fun getNotificationSettings(): NotificationSettings = notificationSettings + override suspend fun getNotificationSettings(): NotificationSettings = notificationSettings override fun encryption(): Encryption = encryption override fun session(): Session = session override fun setDelegate(delegate: ClientDelegate?): TaskHandle = FakeRustTaskHandle() - override fun cachedAvatarUrl(): String? = null + override suspend fun cachedAvatarUrl(): String? = null override suspend fun restoreSession(session: Session) = Unit override fun syncService(): SyncServiceBuilder = FakeRustSyncServiceBuilder() override fun roomDirectorySearch(): RoomDirectorySearch = FakeRustRoomDirectorySearch() @@ -53,4 +57,6 @@ class FakeRustClient( ) = Unit override suspend fun deletePusher(identifiers: PusherIdentifiers) = Unit + override suspend fun clearCaches() = simulateLongTask { clearCachesResult() } + override fun close() = closeResult() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsServiceTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsServiceTest.kt index 83461b9750..34b8c114b6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsServiceTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/notificationsettings/RustNotificationSettingsServiceTest.kt @@ -37,6 +37,7 @@ class RustNotificationSettingsServiceTest { client = FakeRustClient( notificationSettings = notificationSettings, ), + sessionCoroutineScope = this, dispatchers = testCoroutineDispatchers(), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt index 1befd53ae0..33ce5c8c33 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/MatrixRoomInfoMapperTest.kt @@ -33,6 +33,7 @@ import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toPersistentList import org.junit.Test import org.matrix.rustcomponents.sdk.Membership +import uniffi.matrix_sdk_base.EncryptionState import org.matrix.rustcomponents.sdk.JoinRule as RustJoinRule import org.matrix.rustcomponents.sdk.RoomHistoryVisibility as RustRoomHistoryVisibility import org.matrix.rustcomponents.sdk.RoomNotificationMode as RustRoomNotificationMode @@ -48,6 +49,7 @@ class MatrixRoomInfoMapperTest { rawName = "rawName", topic = "topic", avatarUrl = AN_AVATAR_URL, + encryptionState = EncryptionState.ENCRYPTED, isDirect = true, isPublic = false, isSpace = false, @@ -84,7 +86,9 @@ class MatrixRoomInfoMapperTest { rawName = "rawName", topic = "topic", avatarUrl = AN_AVATAR_URL, + isPublic = false, isDirect = true, + isEncrypted = true, isSpace = false, isTombstoned = false, isFavorite = false, @@ -130,6 +134,7 @@ class MatrixRoomInfoMapperTest { rawName = null, topic = null, avatarUrl = null, + encryptionState = EncryptionState.UNKNOWN, isDirect = false, isPublic = true, joinRule = null, @@ -165,6 +170,8 @@ class MatrixRoomInfoMapperTest { rawName = null, topic = null, avatarUrl = null, + isEncrypted = null, + isPublic = true, isDirect = false, joinRule = null, isSpace = false, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt index ede0b4b728..9806cbb1cb 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt @@ -19,6 +19,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SERVER_LIST import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -32,7 +33,9 @@ class DefaultJoinRoomTest { val roomSummary = aRoomSummary() val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } - val roomResult = FakeMatrixRoom() + val roomResult = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo()) + } val aTrigger = JoinedRoom.Trigger.MobilePermalink val client: MatrixClient = FakeMatrixClient().also { it.joinRoomLambda = joinRoomLambda @@ -67,7 +70,9 @@ class DefaultJoinRoomTest { val roomSummary = aRoomSummary() val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } - val roomResult = FakeMatrixRoom() + val roomResult = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo()) + } val aTrigger = JoinedRoom.Trigger.MobilePermalink val client: MatrixClient = FakeMatrixClient().also { it.joinRoomLambda = joinRoomLambda @@ -103,7 +108,9 @@ class DefaultJoinRoomTest { val roomSummary = aRoomSummary() val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } - val roomResult = FakeMatrixRoom() + val roomResult = FakeMatrixRoom().apply { + givenRoomInfo(aRoomInfo()) + } val aTrigger = JoinedRoom.Trigger.MobilePermalink val client: MatrixClient = FakeMatrixClient().also { it.joinRoomLambda = joinRoomLambda 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 578de32a7a..8a77a60975 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 @@ -167,7 +167,7 @@ class FakeMatrixClient( return 0 } - override suspend fun clearCache() { + override suspend fun clearCache() = simulateLongTask { clearCacheLambda() } 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 7d56ab3d7e..25e8a535b0 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 @@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress 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.encryption.RecoveryState +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -25,6 +26,7 @@ class FakeEncryptionService( private val pinUserIdentityResult: (UserId) -> Result = { lambdaError() }, private val isUserVerifiedResult: (UserId) -> Result = { lambdaError() }, private val withdrawVerificationResult: (UserId) -> Result = { lambdaError() }, + private val getUserIdentityResult: (UserId) -> Result = { lambdaError() } ) : EncryptionService { private var disableRecoveryFailure: Exception? = null override val backupStateStateFlow: MutableStateFlow = MutableStateFlow(BackupState.UNKNOWN) @@ -133,6 +135,10 @@ class FakeEncryptionService( isUserVerifiedResult(userId) } + override suspend fun getUserIdentity(userId: UserId): Result = simulateLongTask { + return getUserIdentityResult(userId) + } + companion object { const val FAKE_RECOVERY_KEY = "fake" } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 04c32b5445..7d9bbd1bfc 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.test.room +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback @@ -66,19 +67,9 @@ import java.io.File class FakeMatrixRoom( override val sessionId: SessionId = A_SESSION_ID, override val roomId: RoomId = A_ROOM_ID, - override val displayName: String = "", - override val topic: String? = null, - override val avatarUrl: String? = null, - override var isEncrypted: Boolean = false, - override val canonicalAlias: RoomAlias? = null, - override val alternativeAliases: List = emptyList(), - override val isPublic: Boolean = true, - override val isSpace: Boolean = false, - override val isDirect: Boolean = false, - override val joinedMemberCount: Long = 123L, - override val activeMemberCount: Long = 234L, val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), override val liveTimeline: Timeline = FakeTimeline(), + initialRoomInfo: MatrixRoomInfo = aRoomInfo(), override val roomCoroutineScope: CoroutineScope = TestScope(), private var roomPermalinkResult: () -> Result = { lambdaError() }, private var eventPermalinkResult: (EventId) -> Result = { lambdaError() }, @@ -156,8 +147,8 @@ class FakeMatrixRoom( private val enableEncryptionResult: () -> Result = { lambdaError() }, private val updateJoinRuleResult: (JoinRule) -> Result = { lambdaError() }, ) : MatrixRoom { - private val _roomInfoFlow: MutableSharedFlow = MutableSharedFlow(replay = 1) - override val roomInfoFlow: Flow = _roomInfoFlow + private val _roomInfoFlow: MutableStateFlow = MutableStateFlow(initialRoomInfo) + override val roomInfoFlow: StateFlow = _roomInfoFlow fun givenRoomInfo(roomInfo: MatrixRoomInfo) { _roomInfoFlow.tryEmit(roomInfo) @@ -200,14 +191,14 @@ class FakeMatrixRoom( } override suspend fun updateRoomNotificationSettings(): Result = simulateLongTask { - val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow() + val notificationSettings = notificationSettingsService.getRoomNotificationSettings(roomId, info().isEncrypted.orFalse(), isOneToOne).getOrThrow() roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(notificationSettings) return Result.success(Unit) } override suspend fun enableEncryption(): Result = simulateLongTask { enableEncryptionResult().onSuccess { - isEncrypted = true + givenRoomInfo(info().copy(isEncrypted = true)) emitSyncUpdate() } } @@ -616,6 +607,10 @@ class FakeMatrixRoom( updateJoinRuleResult(joinRule) } + override suspend fun getUpdatedIsEncrypted(): Result = simulateLongTask { + Result.success(info().isEncrypted.orFalse()) + } + fun givenRoomMembersState(state: MatrixRoomMembersState) { membersStateFlow.value = state } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt index 50a4c30a85..cc56c2d32f 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -33,7 +33,9 @@ fun aRoomInfo( rawName: String? = A_ROOM_RAW_NAME, topic: String? = A_ROOM_TOPIC, avatarUrl: String? = AN_AVATAR_URL, + isPublic: Boolean = true, isDirect: Boolean = false, + isEncrypted: Boolean = false, joinRule: JoinRule? = JoinRule.Public, isSpace: Boolean = false, isTombstoned: Boolean = false, @@ -65,7 +67,9 @@ fun aRoomInfo( rawName = rawName, topic = topic, avatarUrl = avatarUrl, + isPublic = isPublic, isDirect = isDirect, + isEncrypted = isEncrypted, joinRule = joinRule, isSpace = isSpace, isTombstoned = isTombstoned, 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 c85fe824d5..ac561c57d2 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 @@ -46,7 +46,9 @@ fun aRoomSummary( rawName: String? = A_ROOM_RAW_NAME, topic: String? = A_ROOM_TOPIC, avatarUrl: String? = null, + isPublic: Boolean = true, isDirect: Boolean = false, + isEncrypted: Boolean = false, joinRule: JoinRule? = JoinRule.Public, isSpace: Boolean = false, isTombstoned: Boolean = false, @@ -80,7 +82,9 @@ fun aRoomSummary( rawName = rawName, topic = topic, avatarUrl = avatarUrl, + isPublic = isPublic, isDirect = isDirect, + isEncrypted = isEncrypted, joinRule = joinRule, isSpace = isSpace, isTombstoned = isTombstoned, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt index 65502a7cd7..d8788e4026 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt @@ -7,11 +7,12 @@ package io.element.android.libraries.matrix.test.verification -import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.VerificationFlowState +import io.element.android.libraries.matrix.api.verification.VerificationRequest import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.Flow @@ -20,13 +21,14 @@ import kotlinx.coroutines.flow.StateFlow class FakeSessionVerificationService( initialSessionVerifiedStatus: SessionVerifiedStatus = SessionVerifiedStatus.Unknown, - private val requestVerificationLambda: () -> Unit = { lambdaError() }, + private val requestCurrentSessionVerificationLambda: () -> Unit = { lambdaError() }, + private val requestUserVerificationLambda: (UserId) -> Unit = { lambdaError() }, private val cancelVerificationLambda: () -> Unit = { lambdaError() }, private val approveVerificationLambda: () -> Unit = { lambdaError() }, private val declineVerificationLambda: () -> Unit = { lambdaError() }, private val startVerificationLambda: () -> Unit = { lambdaError() }, private val resetLambda: (Boolean) -> Unit = { lambdaError() }, - private val acknowledgeVerificationRequestLambda: (SessionVerificationRequestDetails) -> Unit = { lambdaError() }, + private val acknowledgeVerificationRequestLambda: (VerificationRequest.Incoming) -> Unit = { lambdaError() }, private val acceptVerificationRequestLambda: () -> Unit = { lambdaError() }, ) : SessionVerificationService { private val _sessionVerifiedStatus = MutableStateFlow(initialSessionVerifiedStatus) @@ -37,8 +39,12 @@ class FakeSessionVerificationService( override val sessionVerifiedStatus: StateFlow = _sessionVerifiedStatus override val needsSessionVerification: Flow = _needsSessionVerification - override suspend fun requestVerification() { - requestVerificationLambda() + override suspend fun requestCurrentSessionVerification() { + requestCurrentSessionVerificationLambda() + } + + override suspend fun requestUserVerification(userId: UserId) { + requestUserVerificationLambda(userId) } override suspend fun cancelVerification() { @@ -68,8 +74,8 @@ class FakeSessionVerificationService( this.listener = listener } - override suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) { - acknowledgeVerificationRequestLambda(details) + override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) { + acknowledgeVerificationRequestLambda(verificationRequest) } override suspend fun acceptVerificationRequest() = simulateLongTask { diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 8ab1a4b9f5..eb281f09d6 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -40,11 +40,12 @@ fun getRoomMemberAsState(roomMembersState: MatrixRoomMembersState, userId: UserI @Composable fun MatrixRoom.getDirectRoomMember(roomMembersState: MatrixRoomMembersState): State { val roomMembers = roomMembersState.roomMembers() - return remember(roomMembersState) { + val roomInfo by roomInfoFlow.collectAsState() + return remember(roomMembersState, roomInfo.isDirect) { derivedStateOf { roomMembers ?.filter { it.membership.isActive() } - ?.takeIf { it.size == 2 && isDirect } + ?.takeIf { it.size == 2 && roomInfo.isDirect == true } ?.find { it.userId != sessionId } } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index f7f47b7894..e820004b83 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -67,9 +67,9 @@ fun MatrixRoom.canPinUnpin(updateKey: Long): State { } @Composable -fun MatrixRoom.isDmAsState(updateKey: Long): State { - return produceState(initialValue = false, key1 = updateKey) { - value = isDm +fun MatrixRoom.isDmAsState(): State { + return produceState(initialValue = false) { + roomInfoFlow.collect { value = it.isDm } } } @@ -105,25 +105,25 @@ fun MatrixRoom.userPowerLevelAsState(updateKey: Long): State { @Composable fun MatrixRoom.isOwnUserAdmin(): Boolean { - val roomInfo by roomInfoFlow.collectAsState(initial = null) - val powerLevel = roomInfo?.userPowerLevels?.get(sessionId) ?: 0L + val roomInfo by roomInfoFlow.collectAsState() + val powerLevel = roomInfo.userPowerLevels[sessionId] ?: 0L return RoomMember.Role.forPowerLevel(powerLevel) == RoomMember.Role.ADMIN } @Composable fun MatrixRoom.rawName(): String? { - val roomInfo by roomInfoFlow.collectAsState(initial = null) - return roomInfo?.rawName + val roomInfo by roomInfoFlow.collectAsState() + return roomInfo.rawName } @Composable fun MatrixRoom.topic(): String? { - val roomInfo by roomInfoFlow.collectAsState(initial = null) - return roomInfo?.topic + val roomInfo by roomInfoFlow.collectAsState() + return roomInfo.topic } @Composable fun MatrixRoom.avatarUrl(): String? { - val roomInfo by roomInfoFlow.collectAsState(initial = null) - return roomInfo?.avatarUrl + val roomInfo by roomInfoFlow.collectAsState() + return roomInfo.avatarUrl } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerUtils.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt similarity index 69% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerUtils.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt index 48230840b1..7610b55b20 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerUtils.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/ObserveRoomMemberIdentityStateChange.kt @@ -5,21 +5,22 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.messages.impl.messagecomposer +package io.element.android.libraries.matrix.ui.room import androidx.compose.runtime.ProduceStateScope -import io.element.android.features.messages.impl.crypto.identity.IdentityRoomMember -import io.element.android.features.messages.impl.crypto.identity.RoomMemberIdentityStateChange 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.UserId +import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.MatrixRoom 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.PersistentList import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -28,30 +29,33 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @OptIn(ExperimentalCoroutinesApi::class) -fun ProduceStateScope>.observeRoomMemberIdentityStateChange(room: MatrixRoom) { - room.syncUpdateFlow +fun MatrixRoom.roomMemberIdentityStateChange(): Flow> { + return roomInfoFlow .filter { // Room cannot become unencrypted, so we can just apply a filter here. - room.isEncrypted + it.isEncrypted == true } .distinctUntilChanged() .flatMapLatest { - combine(room.identityStateChangesFlow, room.membersStateFlow) { identityStateChanges, membersState -> + combine(identityStateChangesFlow, membersStateFlow) { identityStateChanges, membersState -> identityStateChanges.map { identityStateChange -> val member = membersState.roomMembers() - ?.firstOrNull { roomMember -> roomMember.userId == identityStateChange.userId } + ?.find { roomMember -> roomMember.userId == identityStateChange.userId } ?.toIdentityRoomMember() ?: createDefaultRoomMemberForIdentityChange(identityStateChange.userId) RoomMemberIdentityStateChange( identityRoomMember = member, identityState = identityStateChange.identityState, ) - } - } - .distinctUntilChanged() - .onEach { roomMemberIdentityStateChanges -> - value = roomMemberIdentityStateChanges.toPersistentList() - } + }.toPersistentList() + }.distinctUntilChanged() + } +} + +fun ProduceStateScope>.observeRoomMemberIdentityStateChange(room: MatrixRoom) { + room.roomMemberIdentityStateChange() + .onEach { roomMemberIdentityStateChanges -> + value = roomMemberIdentityStateChanges.toPersistentList() } .launchIn(this) } @@ -72,3 +76,14 @@ private fun createDefaultRoomMemberForIdentityChange(userId: UserId) = IdentityR size = AvatarSize.ComposerAlert, ), ) + +data class RoomMemberIdentityStateChange( + val identityRoomMember: IdentityRoomMember, + val identityState: IdentityState, +) + +data class IdentityRoomMember( + val userId: UserId, + val displayNameOrDefault: String, + val avatarData: AvatarData, +) diff --git a/libraries/matrixui/src/main/res/values-es/translations.xml b/libraries/matrixui/src/main/res/values-es/translations.xml index 2d13ddcf91..5d4ac4cf43 100644 --- a/libraries/matrixui/src/main/res/values-es/translations.xml +++ b/libraries/matrixui/src/main/res/values-es/translations.xml @@ -1,4 +1,7 @@ + "Enviar invitación" + "¿Quieres iniciar un chat con %1$s?" + "¿Enviar invitación?" "%1$s (%2$s) te invitó" diff --git a/libraries/matrixui/src/main/res/values-eu/translations.xml b/libraries/matrixui/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..15f4977395 --- /dev/null +++ b/libraries/matrixui/src/main/res/values-eu/translations.xml @@ -0,0 +1,4 @@ + + + "%1$s(e)k (%2$s) gonbidatu zaitu" + diff --git a/libraries/matrixui/src/main/res/values-fi/translations.xml b/libraries/matrixui/src/main/res/values-fi/translations.xml index 156f7f2ac2..daba3555bb 100644 --- a/libraries/matrixui/src/main/res/values-fi/translations.xml +++ b/libraries/matrixui/src/main/res/values-fi/translations.xml @@ -1,6 +1,7 @@ "Lähetä kutsu" + "Haluaisitko aloittaa keskustelun käyttäjän %1$s kanssa?" "Lähetetäänkö kutsu?" "%1$s (%2$s) kutsui sinut" diff --git a/libraries/matrixui/src/main/res/values-it/translations.xml b/libraries/matrixui/src/main/res/values-it/translations.xml index eb9370de4b..439d61337e 100644 --- a/libraries/matrixui/src/main/res/values-it/translations.xml +++ b/libraries/matrixui/src/main/res/values-it/translations.xml @@ -1,6 +1,7 @@ "Invia invito" + "Vuoi iniziare una conversazione con%1$s?" "Inviare invito?" "%1$s (%2$s) ti ha invitato" diff --git a/libraries/matrixui/src/main/res/values-nb/translations.xml b/libraries/matrixui/src/main/res/values-nb/translations.xml index 08044ee5ea..01bea5c726 100644 --- a/libraries/matrixui/src/main/res/values-nb/translations.xml +++ b/libraries/matrixui/src/main/res/values-nb/translations.xml @@ -1,4 +1,7 @@ + "Send invitasjon" + "Vil du starte en chat med %1$s?" + "Vil du sende invitasjon?" "%1$s(%2$s) inviterte deg" diff --git a/libraries/matrixui/src/main/res/values-sv/translations.xml b/libraries/matrixui/src/main/res/values-sv/translations.xml index 64200445db..7a446ff1a6 100644 --- a/libraries/matrixui/src/main/res/values-sv/translations.xml +++ b/libraries/matrixui/src/main/res/values-sv/translations.xml @@ -1,4 +1,7 @@ + "Skicka inbjudan" + "Vill du starta en chatt med %1$s?" + "Skicka inbjudan?" "%1$s (%2$s) bjöd in dig" diff --git a/libraries/matrixui/src/main/res/values-zh-rTW/translations.xml b/libraries/matrixui/src/main/res/values-zh-rTW/translations.xml index eb6d87524c..f851e399fe 100644 --- a/libraries/matrixui/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/matrixui/src/main/res/values-zh-rTW/translations.xml @@ -1,4 +1,7 @@ + "傳送邀請" + "您想要開始與 %1$s 聊天嗎?" + "傳送邀請?" "%1$s(%2$s)邀請您" diff --git a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembersTest.kt b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembersTest.kt index 4187fe6e90..c54795486f 100644 --- a/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembersTest.kt +++ b/libraries/matrixui/src/test/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembersTest.kt @@ -17,6 +17,7 @@ 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.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest @@ -31,8 +32,10 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emits other member for encrypted DM with 2 joined members`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = true, + initialRoomInfo = aRoomInfo( + isDirect = true, + joinedMembersCount = 2, + ) ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( @@ -47,8 +50,7 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emit null if the room is not a dm`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = false, + initialRoomInfo = aRoomInfo(isDirect = false) ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( @@ -63,8 +65,10 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emits other member even if the room is not encrypted`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = false, - isDirect = true, + initialRoomInfo = aRoomInfo( + isDirect = true, + activeMembersCount = 2, + ) ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( @@ -79,8 +83,7 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emit null if the room has only 1 member`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = true, + initialRoomInfo = aRoomInfo(isDirect = true) ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( @@ -95,9 +98,9 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emit null if the room has only 3 members`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = true, - ) + ).apply { + givenRoomInfo(aRoomInfo(isDirect = true)) + } moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( MatrixRoomMembersState.Ready(persistentListOf(roomMember1, roomMember2, roomMember3)) @@ -111,8 +114,7 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emit null if the other member is not active`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = true, + initialRoomInfo = aRoomInfo(isDirect = true), ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( @@ -132,8 +134,10 @@ class MatrixRoomMembersTest { fun `getDirectRoomMember emit the other member if there are 2 active members`() = runTest { val matrixRoom = FakeMatrixRoom( sessionId = A_USER_ID, - isEncrypted = true, - isDirect = true, + initialRoomInfo = aRoomInfo( + isDirect = true, + activeMembersCount = 2, + ) ) moleculeFlow(RecompositionMode.Immediate) { matrixRoom.getDirectRoomMember( diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt index 7426251ca0..f5440a48b6 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaInfo.kt @@ -156,3 +156,24 @@ fun aVoiceMediaInfo( waveform = waveForm, duration = duration, ) + +fun aTxtMediaInfo( + filename: String = "a text file.txt", + caption: String? = null, + senderName: String? = null, + dateSent: String? = null, + dateSentFull: String? = null, +): MediaInfo = MediaInfo( + filename = filename, + caption = caption, + mimeType = MimeTypes.PlainText, + formattedFileSize = "2kB", + fileExtension = "txt", + senderId = UserId("@alice:server.org"), + senderName = senderName, + senderAvatar = null, + dateSent = dateSent, + dateSentFull = dateSentFull, + waveform = null, + duration = null, +) diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index bbe984fe9c..46a9944a84 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(libs.vanniktech.blurhash) implementation(libs.telephoto.flick) + implementation(projects.features.viewfolder.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.core) 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 6617a8b9cb..f79157e858 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 @@ -64,7 +64,7 @@ class MediaGalleryPresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() var mode by remember { mutableStateOf(MediaGalleryMode.Images) } - val roomInfo by room.roomInfoFlow.collectAsState(null) + val roomInfo by room.roomInfoFlow.collectAsState() var mediaBottomSheetState by remember { mutableStateOf(MediaBottomSheetState.Hidden) } @@ -139,7 +139,7 @@ class MediaGalleryPresenter @AssistedInject constructor( } return MediaGalleryState( - roomName = roomInfo?.name ?: room.displayName, + roomName = roomInfo.name.orEmpty(), mode = mode, groupedMediaItems = groupedMediaItems, mediaBottomSheetState = mediaBottomSheetState, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt index 8120788c5f..3d3276c612 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.viewfolder.api.TextFileViewer import io.element.android.libraries.di.AppScope import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.LocalMediaRenderer @@ -20,7 +21,9 @@ import me.saket.telephoto.zoomable.rememberZoomableState import javax.inject.Inject @ContributesBinding(AppScope::class) -class DefaultLocalMediaRenderer @Inject constructor() : LocalMediaRenderer { +class DefaultLocalMediaRenderer @Inject constructor( + private val textFileViewer: TextFileViewer, +) : LocalMediaRenderer { @Composable override fun Render(localMedia: LocalMedia) { val localMediaViewState = rememberLocalMediaViewState( @@ -33,6 +36,7 @@ class DefaultLocalMediaRenderer @Inject constructor() : LocalMediaRenderer { bottomPaddingInPixels = 0, localMedia = localMedia, localMediaViewState = localMediaViewState, + textFileViewer = textFileViewer, onClick = {} ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt index 8752b19080..10ac3a7c2a 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.local import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import io.element.android.features.viewfolder.api.TextFileViewer import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAudio import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeImage @@ -19,6 +20,7 @@ import io.element.android.libraries.mediaviewer.impl.local.audio.MediaAudioView import io.element.android.libraries.mediaviewer.impl.local.file.MediaFileView import io.element.android.libraries.mediaviewer.impl.local.image.MediaImageView import io.element.android.libraries.mediaviewer.impl.local.pdf.MediaPdfView +import io.element.android.libraries.mediaviewer.impl.local.txt.TextFileView import io.element.android.libraries.mediaviewer.impl.local.video.MediaVideoView @Composable @@ -26,6 +28,7 @@ fun LocalMediaView( localMedia: LocalMedia?, bottomPaddingInPixels: Int, onClick: () -> Unit, + textFileViewer: TextFileViewer, modifier: Modifier = Modifier, isDisplayed: Boolean = true, localMediaViewState: LocalMediaViewState = rememberLocalMediaViewState(), @@ -46,6 +49,11 @@ fun LocalMediaView( localMedia = localMedia, modifier = modifier, ) + mimeType == MimeTypes.PlainText -> TextFileView( + localMedia = localMedia, + textFileViewer = textFileViewer, + modifier = modifier, + ) mimeType == MimeTypes.Pdf -> MediaPdfView( localMediaViewState = localMediaViewState, localMedia = localMedia, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt index b8b87f4605..3493c277cb 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/pdf/PdfViewer.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.roundToPx import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.mediaviewer.impl.viewer.topAppBarHeight import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import me.saket.telephoto.zoomable.zoomable @@ -126,7 +127,7 @@ private fun PdfPagesContentView( ) { // Add a fake item to the top so that the first item is not at the top of the screen. item { - Spacer(modifier = Modifier.height(80.dp)) + Spacer(modifier = Modifier.height(topAppBarHeight)) } items(pdfPages.size) { index -> val pdfPage = pdfPages[index] diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.kt new file mode 100644 index 0000000000..00480f2d42 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileContentProvider.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.mediaviewer.impl.local.txt + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncData +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +open class TextFileContentProvider : PreviewParameterProvider>> { + override val values: Sequence>> + get() = sequenceOf( + AsyncData.Uninitialized, + AsyncData.Loading(), + AsyncData.Success(persistentListOf("Hello, World!")), + AsyncData.Failure(Exception("Failed to load text")), + ) +} diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt new file mode 100644 index 0000000000..c025ab8bed --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/txt/TextFileView.kt @@ -0,0 +1,110 @@ +/* + * 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.mediaviewer.impl.local.txt + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.architecture.AsyncData +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.Text +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.viewer.topAppBarHeight +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList + +@Composable +fun TextFileView( + localMedia: LocalMedia?, + textFileViewer: TextFileViewer, + modifier: Modifier = Modifier, +) { + val data = remember { mutableStateOf>>(AsyncData.Uninitialized) } + val context = LocalContext.current + LaunchedEffect(localMedia?.uri) { + data.value = AsyncData.Loading() + if (localMedia?.uri != null) { + // Load the file content + val result = runCatching { + context.contentResolver.openInputStream(localMedia.uri).use { + it?.bufferedReader()?.readLines()?.toList().orEmpty() + } + } + data.value = if (result.isSuccess) { + AsyncData.Success(result.getOrNull().orEmpty().toImmutableList()) + } else { + AsyncData.Failure(result.exceptionOrNull() ?: Exception("An error occurred")) + } + } + } + TextFileContentView( + data = data.value, + textFileViewer = textFileViewer, + modifier = modifier, + ) +} + +@Composable +private fun TextFileContentView( + data: AsyncData>, + textFileViewer: TextFileViewer, + modifier: Modifier = Modifier, +) { + when (data) { + AsyncData.Uninitialized, + is AsyncData.Loading -> Box( + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + is AsyncData.Failure -> Box( + modifier = modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = data.error.message ?: stringResource(id = CommonStrings.error_unknown)) + } + is AsyncData.Success -> { + textFileViewer.Render( + lines = data.data, + modifier = modifier + .fillMaxSize() + .padding(top = topAppBarHeight), + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun TextFileContentViewPreview( + @PreviewParameter(TextFileContentProvider::class) text: AsyncData>, +) = ElementPreview { + TextFileContentView( + data = text, + textFileViewer = { lines, modifier -> + Text( + modifier = modifier, + text = lines.firstOrNull() ?: "File content" + ) + } + ) +} 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 d994f374f9..3cc44165b0 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 @@ -17,6 +17,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.compound.theme.ForcedDarkElementTheme +import io.element.android.features.viewfolder.api.TextFileViewer import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.RoomScope @@ -42,6 +43,7 @@ class MediaViewerNode @AssistedInject constructor( coroutineDispatchers: CoroutineDispatchers, systemClock: SystemClock, pagerKeysHandler: PagerKeysHandler, + private val textFileViewer: TextFileViewer, ) : Node(buildContext, plugins = plugins), MediaViewerNavigator { private val inputs = inputs() @@ -78,10 +80,7 @@ class MediaViewerNode @AssistedInject constructor( } when (timelineMode) { null -> timelineMediaGalleryDataSource - Timeline.Mode.LIVE -> { - // Even if the timelineMediaGalleryDataSource does not know the eventId, the SDK will create the timeline faster - timelineMediaGalleryDataSource - } + Timeline.Mode.LIVE, Timeline.Mode.FOCUSED_ON_EVENT -> { // Does timelineMediaGalleryDataSource knows the eventId? val lastData = timelineMediaGalleryDataSource.getLastData().dataOrNull() @@ -128,6 +127,7 @@ class MediaViewerNode @AssistedInject constructor( val state = presenter.present() MediaViewerView( state = state, + textFileViewer = textFileViewer, modifier = modifier, onBackClick = ::onDone ) 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 834edacd76..f897984578 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 @@ -164,7 +164,9 @@ class MediaViewerPresenter @AssistedInject constructor( ) { val isRenderingLoadingBackward by remember { derivedStateOf { - currentIndex.intValue == data.value.lastIndex && data.value.lastOrNull() is MediaViewerPageData.Loading + currentIndex.intValue == data.value.lastIndex && + data.value.size > 1 && + data.value.lastOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingBackward) { @@ -186,7 +188,9 @@ class MediaViewerPresenter @AssistedInject constructor( ) { val isRenderingLoadingForward by remember { derivedStateOf { - currentIndex.intValue == 0 && data.value.firstOrNull() is MediaViewerPageData.Loading + currentIndex.intValue == 0 && + data.value.size > 1 && + data.value.firstOrNull() is MediaViewerPageData.Loading } } if (isRenderingLoadingForward) { 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 95557c13d6..6686cd9fce 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 @@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.aPdfMediaInfo +import io.element.android.libraries.mediaviewer.api.aTxtMediaInfo import io.element.android.libraries.mediaviewer.api.aVideoMediaInfo import io.element.android.libraries.mediaviewer.api.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo @@ -159,6 +160,14 @@ open class MediaViewerStateProvider : PreviewParameterProvider MediaViewerPageData.Failure(Exception("error")) ), ), + aMediaViewerState( + listOf( + aMediaViewerPageData( + downloadedMedia = AsyncData.Loading(), + mediaInfo = aTxtMediaInfo(), + ) + ) + ), ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index 5ebe8e8087..501c434674 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -49,6 +49,7 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.viewfolder.api.TextFileViewer import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeVideo @@ -83,9 +84,12 @@ import me.saket.telephoto.zoomable.OverzoomEffect import me.saket.telephoto.zoomable.ZoomSpec import me.saket.telephoto.zoomable.rememberZoomableState +val topAppBarHeight = 88.dp + @Composable fun MediaViewerView( state: MediaViewerState, + textFileViewer: TextFileViewer, onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -143,6 +147,7 @@ fun MediaViewerView( showOverlay = showOverlay, bottomPaddingInPixels = bottomPaddingInPixels, data = dataForPage, + textFileViewer = textFileViewer, onDismiss = onBackClick, onRetry = { state.eventSink(MediaViewerEvents.LoadMedia(dataForPage)) @@ -267,6 +272,7 @@ private fun MediaViewerPage( showOverlay: Boolean, bottomPaddingInPixels: Int, data: MediaViewerPageData.MediaViewerData, + textFileViewer: TextFileViewer, onDismiss: () -> Unit, onRetry: () -> Unit, onDismissError: () -> Unit, @@ -316,6 +322,7 @@ private fun MediaViewerPage( localMediaViewState = localMediaViewState, localMedia = downloadedMedia.dataOrNull(), mediaInfo = data.mediaInfo, + textFileViewer = textFileViewer, onClick = { if (playableState is PlayableState.NotPlayable) { currentOnShowOverlayChange(!currentShowOverlay) @@ -563,6 +570,7 @@ private fun ErrorView( internal fun MediaViewerViewPreview(@PreviewParameter(MediaViewerStateProvider::class) state: MediaViewerState) = ElementPreviewDark { MediaViewerView( state = state, + textFileViewer = { _, _ -> }, onBackClick = {} ) } diff --git a/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..686dc60658 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,21 @@ + + + "Este archivo se eliminará de la sala y los miembros no tendrán acceso a él." + "¿Borrar archivo?" + "Verifica tu conexión a Internet e inténtalo de nuevo." + "Los documentos, archivos de audio y mensajes de voz subidos a esta sala se mostrarán aquí." + "Aún no se ha subido ningún archivo" + "Cargando archivos…" + "Cargando medios…" + "Archivos" + "Medios" + "Las imágenes y vídeos subidos a esta sala se mostrarán aquí." + "Aún no se ha subido ningún medio" + "Medios y archivos" + "Formato de archivo" + "Nombre del archivo" + "No hay más archivos que mostrar" + "No hay más medios que mostrar" + "Subido por" + "Subido el" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-eu/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..696d7ceee3 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,18 @@ + + + "Fitxategia gelatik kenduko da eta kideek ezingo dute atzitu." + "Fitxategia ezabatu?" + "Gela honetara igotako dokumentuak, audio-fitxategiak, eta ahots-mezuak hemen erakutsiko dira." + "Oraindik ez da fitxategirik igo" + "Fitxategiak kargatzen…" + "Multimedia kargatzen…" + "Fitxategiak" + "Multimedia" + "Gela honetara igotako irudiak eta bideoak hemen erakutsiko dira." + "Oraindik ez da multimedia fitxategirik igo" + "Multimedia eta fitxategiak" + "Fitxategiaren formatua" + "Fitxategiaren izena" + "Nork igota:" + "Noiz igota:" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-nb/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-nb/translations.xml new file mode 100644 index 0000000000..8cc6eb3c84 --- /dev/null +++ b/libraries/mediaviewer/impl/src/main/res/values-nb/translations.xml @@ -0,0 +1,21 @@ + + + "Denne filen vil bli fjernet fra rommet, og medlemmene vil ikke lenger ha tilgang til den." + "Vil du slette filen?" + "Sjekk internettforbindelsen din og prøv igjen." + "Dokumenter, lydfiler og talemeldinger som lastes opp til dette rommet, vises her." + "Ingen filer lastet opp ennå" + "Laster inn filer…" + "Laster inn medier…" + "Filer" + "Mediefiler" + "Bilder og videoer som lastes opp til dette rommet, vises her." + "Ingen mediefiler lastet opp ennå" + "Medier og filer" + "Filformat" + "Filnavn" + "Ingen flere filer å vise" + "Ikke flere mediefiler å vise" + "Lastet opp av" + "Lastet opp den" + diff --git a/libraries/mediaviewer/impl/src/main/res/values-sv/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-sv/translations.xml index fa5305ac47..ac1040d60e 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-sv/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-sv/translations.xml @@ -2,6 +2,7 @@ "Den här filen kommer att tas bort från rummet och medlemmar kommer inte att ha tillgång till den." "Radera fil?" + "Kontrollera din internetanslutning och försök igen." "Dokument, ljudfiler och röstmeddelanden som laddas upp till detta rum visas här." "Inga filer uppladdade än" "Laddar in filer…" @@ -13,6 +14,8 @@ "Media och filer" "Filformat" "Filnamn" + "Inga fler filer att visa" + "Ingen mer media att visa" "Uppladdad av" "Uppladdad på" diff --git a/libraries/mediaviewer/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/mediaviewer/impl/src/main/res/values-zh-rTW/translations.xml index 9f0ec12011..4cbe579e00 100644 --- a/libraries/mediaviewer/impl/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/mediaviewer/impl/src/main/res/values-zh-rTW/translations.xml @@ -14,6 +14,8 @@ "媒體與檔案" "檔案格式" "檔案名稱" + "無可顯示的檔案" + "無可顯示的媒體" "上傳者:" "上傳於" diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt index 81e270a09f..56396ca2a6 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryPresenterTest.kt @@ -20,6 +20,7 @@ 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.media.FakeMatrixMediaLoader import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.mediaviewer.impl.datasource.FakeMediaGalleryDataSource import io.element.android.libraries.mediaviewer.impl.datasource.MediaGalleryDataSource @@ -51,7 +52,7 @@ class MediaGalleryPresenterTest { startLambda = startLambda, ), room = FakeMatrixRoom( - displayName = A_ROOM_NAME, + initialRoomInfo = aRoomInfo(name = A_ROOM_NAME), createTimelineResult = { Result.success(FakeTimeline()) }, ) ) @@ -70,7 +71,7 @@ class MediaGalleryPresenterTest { fun `present - change mode`() = runTest { val presenter = createMediaGalleryPresenter( room = FakeMatrixRoom( - displayName = A_ROOM_NAME, + initialRoomInfo = aRoomInfo(name = A_ROOM_NAME), createTimelineResult = { Result.success(FakeTimeline()) }, ) ) @@ -100,7 +101,7 @@ class MediaGalleryPresenterTest { val presenter = createMediaGalleryPresenter( room = FakeMatrixRoom( sessionId = A_USER_ID, - displayName = A_ROOM_NAME, + initialRoomInfo = aRoomInfo(name = A_ROOM_NAME), createTimelineResult = { Result.success(FakeTimeline()) }, canRedactOwnResult = { Result.success(canDeleteOwn) } ) @@ -143,7 +144,7 @@ class MediaGalleryPresenterTest { val presenter = createMediaGalleryPresenter( room = FakeMatrixRoom( sessionId = A_USER_ID, - displayName = A_ROOM_NAME, + initialRoomInfo = aRoomInfo(name = A_ROOM_NAME), createTimelineResult = { Result.success(FakeTimeline()) }, canRedactOtherResult = { Result.success(canDeleteOther) } ) @@ -176,7 +177,7 @@ class MediaGalleryPresenterTest { fun `present - delete bottom sheet`() = runTest { val presenter = createMediaGalleryPresenter( room = FakeMatrixRoom( - displayName = A_ROOM_NAME, + initialRoomInfo = aRoomInfo(name = A_ROOM_NAME), createTimelineResult = { Result.success(FakeTimeline()) }, ) ) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt index bfc294098b..8bd23c2089 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerViewTest.kt @@ -252,6 +252,7 @@ private fun AndroidComposeTestRule.setMedia setContent { MediaViewerView( state = state, + textFileViewer = { _, _ -> }, onBackClick = onBackClick, ) } diff --git a/libraries/permissions/api/src/main/res/values-eu/translations.xml b/libraries/permissions/api/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..00819b0db2 --- /dev/null +++ b/libraries/permissions/api/src/main/res/values-eu/translations.xml @@ -0,0 +1,7 @@ + + + "Aplikazioak kamera erabiltzeko, eman baimena sistemaren ezarpenetan." + "Eman baimena sistemaren ezarpenetan." + "Aplikazioak mikrofonoa erabiltzeko, eman baimena sistemaren ezarpenetan." + "Aplikazioak jakinarazpenak bistaratzeko, eman baimena sistemaren ezarpenetan." + diff --git a/libraries/permissions/impl/src/main/res/values-es/translations.xml b/libraries/permissions/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..45d5eaf9e0 --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,5 @@ + + + "Verificar que la aplicación pueda mostrar notificaciones." + "Verificar permisos" + diff --git a/libraries/permissions/impl/src/main/res/values-eu/translations.xml b/libraries/permissions/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..9f5c8edabc --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,5 @@ + + + "Egiaztatu aplikazioak jakinarazpenak erakutsi ditzakeela." + "Egiaztatu baimenak" + diff --git a/libraries/permissions/impl/src/main/res/values-ka/translations.xml b/libraries/permissions/impl/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..2dbb4018f7 --- /dev/null +++ b/libraries/permissions/impl/src/main/res/values-ka/translations.xml @@ -0,0 +1,5 @@ + + + "შეამოწმეთ რომ აპლიკაციას შეტყობინებების ჩვენება შეუძლია." + "ნებართვების შემოწმება" + diff --git a/libraries/permissions/impl/src/main/res/values-nb/translations.xml b/libraries/permissions/impl/src/main/res/values-nb/translations.xml index b64b4b2b42..b53212a8e4 100644 --- a/libraries/permissions/impl/src/main/res/values-nb/translations.xml +++ b/libraries/permissions/impl/src/main/res/values-nb/translations.xml @@ -1,4 +1,5 @@ + "Kontroller at programmet kan vise varsler." "Sjekk tillatelser" diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index ef85740f68..1c13a67ae5 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -27,7 +27,6 @@ dependencies { implementation(libs.dagger) implementation(libs.androidx.corektx) implementation(libs.androidx.datastore.preferences) - implementation(libs.androidx.security.crypto) implementation(platform(libs.network.retrofit.bom)) implementation(libs.network.retrofit) implementation(libs.serialization.json) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt index 070f876973..aea52c8b78 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt @@ -152,8 +152,8 @@ class NotificationBroadcastReceiverHandler @Inject constructor( imageUriString = null, imageMimeType = null, threadId = threadId, - roomName = room.displayName, - roomIsDm = room.isDm, + roomName = room.info().name, + roomIsDm = room.isDm(), outGoingMessage = true, ) onNotifiableEventReceived.onNotifiableEventReceived(notifiableMessageEvent) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt index 56c0a45fd5..f8cf2836b8 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt @@ -119,7 +119,7 @@ class DefaultNotificationCreator @Inject constructor( else -> pendingIntentFactory.createOpenRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId) } - val smallIcon = CommonDrawables.ic_notification_small + val smallIcon = CommonDrawables.ic_notification val containsMissedCall = events.any { it.type == EventType.CALL_NOTIFY } val channelId = if (containsMissedCall) { @@ -219,7 +219,7 @@ class DefaultNotificationCreator @Inject constructor( override fun createRoomInvitationNotification( inviteNotifiableEvent: InviteNotifiableEvent ): Notification { - val smallIcon = CommonDrawables.ic_notification_small + val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) @@ -261,7 +261,7 @@ class DefaultNotificationCreator @Inject constructor( override fun createSimpleEventNotification( simpleNotifiableEvent: SimpleNotifiableEvent, ): Notification { - val smallIcon = CommonDrawables.ic_notification_small + val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) @@ -294,7 +294,7 @@ class DefaultNotificationCreator @Inject constructor( override fun createFallbackNotification( fallbackNotifiableEvent: FallbackNotifiableEvent, ): Notification { - val smallIcon = CommonDrawables.ic_notification_small + val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(false) return NotificationCompat.Builder(context, channelId) @@ -330,7 +330,7 @@ class DefaultNotificationCreator @Inject constructor( noisy: Boolean, lastMessageTimestamp: Long ): Notification { - val smallIcon = CommonDrawables.ic_notification_small + val smallIcon = CommonDrawables.ic_notification val channelId = notificationChannels.getChannelIdForMessage(noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) @@ -367,7 +367,7 @@ class DefaultNotificationCreator @Inject constructor( return NotificationCompat.Builder(context, notificationChannels.getChannelIdForTest()) .setContentTitle(buildMeta.applicationName) .setContentText(stringProvider.getString(R.string.notification_test_push_notification_content)) - .setSmallIcon(CommonDrawables.ic_notification_small) + .setSmallIcon(CommonDrawables.ic_notification) .setLargeIcon(getBitmap(R.drawable.element_logo_green)) .setColor(accentColor) .setPriority(NotificationCompat.PRIORITY_MAX) diff --git a/libraries/push/impl/src/main/res/values-es/translations.xml b/libraries/push/impl/src/main/res/values-es/translations.xml index 27533f254c..0fcab9de6d 100644 --- a/libraries/push/impl/src/main/res/values-es/translations.xml +++ b/libraries/push/impl/src/main/res/values-es/translations.xml @@ -3,6 +3,7 @@ "Llamada" "Esperando eventos" "Notificaciones ruidosas" + "Llamadas entrantes" "Notificaciones silenciosas" "%1$s: %2$d mensaje" @@ -13,13 +14,16 @@ "%d notificaciones" "Notificación" + "📹 Llamada entrante" "** No se ha podido enviar - por favor, abre la sala" "Unirse" + "Rechazar" "%d invitación" "%d invitaciones" "Te invitó a chatear" + "%1$s te invitó a chatear" "Te mencionó: %1$s" "Mensajes nuevos" @@ -30,7 +34,9 @@ "Marcar como leído" "Respuesta rápida" "Te invitó a unirte a la sala" + "%1$s te invitó a unirte a la sala" "Yo" + "%1$s mencionó o respondió" "¡Estás viendo la notificación! ¡Haz clic en mí!" "%1$s: %2$s" "%1$s: %2$s %3$s" @@ -48,4 +54,28 @@ "Sincronización en segundo plano" "Servicios de Google" "No se han encontrado Servicios de Google Play válidos. Es posible que las notificaciones no funcionen correctamente." + "Obtener el nombre del proveedor actual." + "No se ha seleccionado ningún proveedor de push." + "Proveedor de push actual: %1$s." + "Proveedor de push actual" + "Asegurarse de que la aplicación tiene al menos un proveedor de push." + "No se encontró ningún proveedor de push." + + "Se encontró %1$d proveedor de push: %2$s" + "Se encontraron %1$d proveedores de push: %2$s" + + "Detectar proveedores de push" + "Verificar que la aplicación pueda mostrar notificaciones." + "No se ha hecho clic en la notificación." + "No se puede mostrar la notificación." + "¡Se ha hecho clic en la notificación!" + "Mostrar notificación" + "Haz clic en la notificación para continuar la prueba." + "Asegurarse de que la aplicación esté recibiendo notificaciones push." + "Error: el servicio de push ha rechazado la solicitud." + "Error: %1$s." + "Error, no se puede probar el push." + "Error, tiempo de espera agotado para push." + "Envío y recepción de notificación push tomó %1$d ms." + "Probar envío y recepción Push" diff --git a/libraries/push/impl/src/main/res/values-eu/translations.xml b/libraries/push/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..288ae15d4c --- /dev/null +++ b/libraries/push/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,73 @@ + + + "Deia" + "Gertaerei adi" + "Jakinarazpen zaratatsuak" + "Jakinarazpen isilak" + + "%1$s: mezu %2$d" + "%1$s: %2$d mezu" + + + "jakinarazpen %d" + "%d jakinarazpen" + + "Jakinarazpena" + "Deia jasotzen" + "** Huts egin du bidalketak - ireki gela" + "Elkartu" + "Baztertu" + + "Gonbidapen %d" + "%d gonbidapen" + + "Txatetzera gonbidatu zaitu" + "Aipatu zaitu: %1$s" + "Mezu berriak" + + "Mezu berri %d" + "%d mezu berri" + + "%1$s (r)ekin erreakzionatu du" + "Markatu irakurritzat" + "Erantzun azkarra" + "Gelan sartzera gonbidatu zaitu" + "%1$s(e)k gelan sartzera gonbidatu zaitu" + "Neu" + "%1$s(e)k aipatu zaitu edo erantzun dizu" + "Jakinarazpena ikusten ari zara! Klikatu!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + + "Irakurri gabeko mezu %den jakinarazpena" + "Irakurri gabeko %d mezuren jakinarazpena" + + "%1$s %2$s gelan" + "%1$s %2$s gelan eta %3$s" + + "Gela %d" + "%d gela" + + "Atzeko planoko sinkronizazioa" + "Google Services" + "Ez da baliozko Google Play Servicerik aurkitu. Litekeena da jakinarazpenak behar bezala ez ibiltzea." + "Lortu uneko hornitzailearen izena." + "Ez da push hornitzailerik hautatu." + "Uneko push hornitzailea: %1$s." + "Uneko push hornitzailea" + "Ez da push hornitzailerik aurkitu." + + "Push hornitzaile %1$d aurkitu da: %2$s" + "%1$d push hornitzaile aurkitu dira: %2$s" + + "Detektatu push hornitzaileak" + "Egiaztatu aplikazioak jakinarazpena bistaratu dezakeela." + "Ez da klikik egin jakinarazpenean." + "Ezin da jakinarazpena bistaratu." + "Klik egin da jakinarazpenean!" + "Bistaratu jakinarazpena" + "Klikatu jakinarazpenean probarekin jarraitzeko." + "Errorea: %1$s." + "Errorea, ezin da push-a probatu." + "Errorea, denbora agortu da push-aren zain." + diff --git a/libraries/push/impl/src/main/res/values-ka/translations.xml b/libraries/push/impl/src/main/res/values-ka/translations.xml index 8296075572..a49f803c2a 100644 --- a/libraries/push/impl/src/main/res/values-ka/translations.xml +++ b/libraries/push/impl/src/main/res/values-ka/translations.xml @@ -27,6 +27,7 @@ "%d ახალი მესიჯი" "რეაგირება მოხდა: %1$s" + "წაკითხულად მონიშვნა" "Სწრაფი პასუხი" "მოგიწვიათ ოთახში" "მე" @@ -47,4 +48,28 @@ "ფონის სინქრონიზაცია" "Google სერვისები" "მოქმედი Google Play სერვისები ვერ მოიძებნა. შეტყობინებები შეიძლება ვერ იმუშაოს სწორად." + "მიმდინარე პროვაიდერის სახელის გაგება" + "push პროვაიდერები არაა არჩეული." + "მიმდინარე push პროვაიდერი: %1$s." + "მიმდინარე push პროვაიდერი" + "დარწმუნდით რომ აპლიკაციას მინიმუმ ერთი push პროვაიდერი." + "push პროვაიდერები ვერ მოიძებნა." + + "მოიძებნა %1$d push პროვაიდერი: %2$s" + "მოიძებნა %1$d push პროვაიდერი: %2$s" + + "push პროვაიდერების აღმოჩენა" + "შეამოწმეთ აპლიკაციას თუ შეუძლია შეტყობინებების ჩვენება" + "შეტყობინება არ იქნა დაჭერილი." + "შეტყობინების ჩვენება შეუძლებელია." + "შეტყობინება იყო დაჭერილი!" + "შეტყობინებების ჩვენება" + "გთხოვთ დააჭიროთ შეტყობინებაზე ტესტის გასაგრძელებლად." + "დარწმუნდით რომ აპლიკაცია იღებს push-შეტყობინებას." + "შეცდომა: გამგზავნმა უარყო მოთხოვნა." + "შეცდომა: %1$s." + "შეცდომა, push-ის ტესტირება შეუძლებელია." + "შეცდომა, push-ისთვის ლოდინის დრო გავიდა." + "Push-შეტყობინების ციკლმა დაიკავა %1$d ms." + "push-შეტყობინების ციკლის ტესტირება" 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 7ac811205f..57b2aa3fe9 100644 --- a/libraries/push/impl/src/main/res/values-nb/translations.xml +++ b/libraries/push/impl/src/main/res/values-nb/translations.xml @@ -12,7 +12,7 @@ "%d varsler" "Varsel" - "Innkommende anrop" + "📹 Innkommende anrop" "** Kunne ikke sende - vennligst åpne rommet" "Bli med" "Avvis" @@ -22,6 +22,7 @@ "Inviterte deg til å chatte" "%1$s inviterte deg til å chatte" + "Nevnte deg: %1$s" "Nye meldinger" "%d ny melding" @@ -51,9 +52,19 @@ "Bakgrunnssynkronisering" "Google Services" "Ingen gyldige Google Play-tjenester funnet. Det kan hende at varsler ikke fungerer som de skal." + "Få navnet på den nåværende tilbyderen." "Ingen push-leverandører er valgt." "Gjeldende push-leverandør: %1$s." "Nåværende push-leverandør" "Ingen push-leverandører funnet." + "Kontroller at programmet kan vise varsler." + "Det er ikke klikket på varselet." + "Kan ikke vise varselet." + "Varselet har blitt klikket på!" + "Vis varsel" + "Klikk på varselet for å fortsette testen." + "Kontroller at applikasjonen mottar push." + "Feil: pusheren har avvist forespørselen." "Feil: %1$s." + "Feil, kan ikke teste push." diff --git a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml index 90717e73a1..e02f393f9a 100644 --- a/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/push/impl/src/main/res/values-zh-rTW/translations.xml @@ -12,7 +12,7 @@ "%d 個通知" "通知" - "來電" + "📹 來電" "** 無法傳送,請開啟聊天室" "加入" "拒絕" diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt index 17093fef43..0ce6038d79 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt @@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.preferences.api.store.SessionPreferencesStore @@ -337,7 +338,14 @@ class NotificationBroadcastReceiverHandlerTest { val matrixRoom = FakeMatrixRoom( liveTimeline = liveTimeline, getUpdatedMemberResult = { Result.success(aRoomMember()) }, - ) + ).apply { + givenRoomInfo( + aRoomInfo( + isDirect = true, + activeMembersCount = 2, + ) + ) + } val onNotifiableEventReceivedResult = lambdaRecorder { _ -> } val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult) val sut = createNotificationBroadcastReceiverHandler( @@ -396,7 +404,14 @@ class NotificationBroadcastReceiverHandlerTest { val matrixRoom = FakeMatrixRoom( liveTimeline = liveTimeline, getUpdatedMemberResult = { Result.success(aRoomMember()) }, - ) + ).apply { + givenRoomInfo( + aRoomInfo( + isDirect = true, + activeMembersCount = 2, + ) + ) + } val onNotifiableEventReceivedResult = lambdaRecorder { _ -> } val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult) val sut = createNotificationBroadcastReceiverHandler( diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index 044b930572..735de54d11 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -50,6 +50,7 @@ setupAnvil() dependencies { implementation(libs.dagger) implementation(libs.androidx.corektx) + implementation(projects.features.enterprise.api) implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.libraries.di) @@ -73,6 +74,8 @@ dependencies { testImplementation(libs.test.truth) testImplementation(libs.test.turbine) testImplementation(libs.test.robolectric) + testImplementation(libs.kotlinx.collections.immutable) + testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseGatewayProvider.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseGatewayProvider.kt new file mode 100644 index 0000000000..2b378766bc --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseGatewayProvider.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.pushproviders.firebase + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +interface FirebaseGatewayProvider { + fun getFirebaseGateway(): String +} + +@ContributesBinding(AppScope::class) +class DefaultFirebaseGatewayProvider @Inject constructor( + private val enterpriseService: EnterpriseService, +) : FirebaseGatewayProvider { + override fun getFirebaseGateway(): String { + return enterpriseService.firebasePushGateway() ?: FirebaseConfig.PUSHER_HTTP_URL + } +} diff --git a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt index bf616b7de3..e5589af493 100644 --- a/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt +++ b/libraries/pushproviders/firebase/src/main/kotlin/io/element/android/libraries/pushproviders/firebase/FirebaseNewTokenHandler.kt @@ -36,6 +36,7 @@ class DefaultFirebaseNewTokenHandler @Inject constructor( private val userPushStoreFactory: UserPushStoreFactory, private val matrixClientProvider: MatrixClientProvider, private val firebaseStore: FirebaseStore, + private val firebaseGatewayProvider: FirebaseGatewayProvider, ) : FirebaseNewTokenHandler { override suspend fun handle(firebaseToken: String) { firebaseStore.storeFcmToken(firebaseToken) @@ -55,7 +56,7 @@ class DefaultFirebaseNewTokenHandler @Inject constructor( .registerPusher( matrixClient = client, pushKey = firebaseToken, - gateway = FirebaseConfig.PUSHER_HTTP_URL, + gateway = firebaseGatewayProvider.getFirebaseGateway(), ) .onFailure { Timber.tag(loggerTag.value).e(it, "Failed to register pusher for session $sessionId") 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 0b12e00bbd..a6be83e803 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 @@ -27,6 +27,7 @@ class FirebasePushProvider @Inject constructor( private val pusherSubscriber: PusherSubscriber, private val isPlayServiceAvailable: IsPlayServiceAvailable, private val firebaseTokenRotator: FirebaseTokenRotator, + private val firebaseGatewayProvider: FirebaseGatewayProvider, ) : PushProvider { override val index = FirebaseConfig.INDEX override val name = FirebaseConfig.NAME @@ -48,7 +49,7 @@ class FirebasePushProvider @Inject constructor( return pusherSubscriber.registerPusher( matrixClient = matrixClient, pushKey = pushKey, - gateway = FirebaseConfig.PUSHER_HTTP_URL, + gateway = firebaseGatewayProvider.getFirebaseGateway(), ) } @@ -60,7 +61,7 @@ class FirebasePushProvider @Inject constructor( Timber.tag(loggerTag.value).w("Unable to unregister pusher, Firebase token is not known.") Result.success(Unit) } else { - pusherSubscriber.unregisterPusher(matrixClient, pushKey, FirebaseConfig.PUSHER_HTTP_URL) + pusherSubscriber.unregisterPusher(matrixClient, pushKey, firebaseGatewayProvider.getFirebaseGateway()) } } @@ -72,7 +73,7 @@ class FirebasePushProvider @Inject constructor( override suspend fun getCurrentUserPushConfig(): CurrentUserPushConfig? { return firebaseStore.getFcmToken()?.let { fcmToken -> CurrentUserPushConfig( - url = FirebaseConfig.PUSHER_HTTP_URL, + url = firebaseGatewayProvider.getFirebaseGateway(), pushKey = fcmToken ) } diff --git a/libraries/pushproviders/firebase/src/main/res/values-es/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..d6a3de2d3c --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-es/translations.xml @@ -0,0 +1,11 @@ + + + "Asegurarse de que Firebase esté disponible." + "Firebase no está disponible." + "Firebase está disponible." + "Verificar Firebase" + "Asegurarse de que el token de Firebase esté disponible." + "Se desconoce el token de Firebase." + "Token de Firebase: %1$s." + "Verificar token de Firebase" + diff --git a/libraries/pushproviders/firebase/src/main/res/values-eu/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..e67e55ec2d --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-eu/translations.xml @@ -0,0 +1,11 @@ + + + "Ziurtatu Firebase erabilgarri dagoela." + "Firebase ez dago erabilgarri." + "Firebase eskuragarri dago." + "Egiaztatu Firebase" + "Ziurtatu Firebaseren tokena erabilgarri dagoela." + "Ez da Firebaseren tokena ezagutzen." + "Firebaseren tokena: %1$s." + "Egiaztatu Firebaseren tokena" + diff --git a/libraries/pushproviders/firebase/src/main/res/values-ka/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..eb3645b510 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-ka/translations.xml @@ -0,0 +1,11 @@ + + + "დარწმუნდით რომ Firebase ხელმისაწვდომია." + "Firebase არაა ხელმისაწვდომი." + "Firebase ხელმისაწვდომია." + "Firebase-ის შემოწმება" + "დარწმუნდით რომ Firebase ტოკენი ხელმისაწვდომია." + "Firebase ტოკენი უცნობია." + "Firebase ტოკენი: %1$s." + "შეამოწმეთ Firebase ტოკენი" + diff --git a/libraries/pushproviders/firebase/src/main/res/values-nb/translations.xml b/libraries/pushproviders/firebase/src/main/res/values-nb/translations.xml new file mode 100644 index 0000000000..9bbff0e538 --- /dev/null +++ b/libraries/pushproviders/firebase/src/main/res/values-nb/translations.xml @@ -0,0 +1,11 @@ + + + "Sørg for at Firebase er tilgjengelig." + "Firebase er ikke tilgjengelig." + "Firebase er tilgjengelig." + "Sjekk Firebase" + "Sørg for at Firebase-token er tilgjengelig." + "Firebase-token er ikke kjent." + "Firebase-token: %1$s." + "Sjekk Firebase-token" + diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt index 08e9c5be36..c8c5dfa9bf 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/DefaultFirebaseNewTokenHandlerTest.kt @@ -79,8 +79,8 @@ class DefaultFirebaseNewTokenHandlerTest { registerPusherResult.assertions() .isCalledExactly(2) .withSequence( - listOf(value(aMatrixClient1), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)), - listOf(value(aMatrixClient3), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)), + listOf(value(aMatrixClient1), value("aToken"), value(A_FIREBASE_GATEWAY)), + listOf(value(aMatrixClient3), value("aToken"), value(A_FIREBASE_GATEWAY)), ) } @@ -130,7 +130,7 @@ class DefaultFirebaseNewTokenHandlerTest { registerPusherResult.assertions() registerPusherResult.assertions() .isCalledOnce() - .with(value(aMatrixClient1), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) + .with(value(aMatrixClient1), value("aToken"), value(A_FIREBASE_GATEWAY)) } private fun createDefaultFirebaseNewTokenHandler( @@ -139,13 +139,15 @@ class DefaultFirebaseNewTokenHandlerTest { userPushStoreFactory: UserPushStoreFactory = FakeUserPushStoreFactory(), matrixClientProvider: MatrixClientProvider = FakeMatrixClientProvider(), firebaseStore: FirebaseStore = InMemoryFirebaseStore(), + firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider(), ): FirebaseNewTokenHandler { return DefaultFirebaseNewTokenHandler( pusherSubscriber = pusherSubscriber, sessionStore = sessionStore, userPushStoreFactory = userPushStoreFactory, matrixClientProvider = matrixClientProvider, - firebaseStore = firebaseStore + firebaseStore = firebaseStore, + firebaseGatewayProvider = firebaseGatewayProvider, ) } } diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseGatewayProvider.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseGatewayProvider.kt new file mode 100644 index 0000000000..3540562a3d --- /dev/null +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FakeFirebaseGatewayProvider.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.libraries.pushproviders.firebase + +const val A_FIREBASE_GATEWAY = "aGateway" + +class FakeFirebaseGatewayProvider( + private val firebaseGatewayResult: () -> String = { A_FIREBASE_GATEWAY } +) : FirebaseGatewayProvider { + override fun getFirebaseGateway() = firebaseGatewayResult() +} diff --git a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt index fbb945106a..53ed52be07 100644 --- a/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt +++ b/libraries/pushproviders/firebase/src/test/kotlin/io/element/android/libraries/pushproviders/firebase/FirebasePushProviderTest.kt @@ -70,7 +70,7 @@ class FirebasePushProviderTest { assertThat(result).isEqualTo(Result.success(Unit)) registerPusherResultLambda.assertions() .isCalledOnce() - .with(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) + .with(value(matrixClient), value("aToken"), value(A_FIREBASE_GATEWAY)) } @Test @@ -117,7 +117,7 @@ class FirebasePushProviderTest { assertThat(result).isEqualTo(Result.success(Unit)) unregisterPusherResultLambda.assertions() .isCalledOnce() - .with(value(matrixClient), value("aToken"), value(FirebaseConfig.PUSHER_HTTP_URL)) + .with(value(matrixClient), value("aToken"), value(A_FIREBASE_GATEWAY)) } @Test @@ -164,7 +164,7 @@ class FirebasePushProviderTest { ), ) val result = firebasePushProvider.getCurrentUserPushConfig() - assertThat(result).isEqualTo(CurrentUserPushConfig(FirebaseConfig.PUSHER_HTTP_URL, "aToken")) + assertThat(result).isEqualTo(CurrentUserPushConfig(A_FIREBASE_GATEWAY, "aToken")) } @Test @@ -194,12 +194,14 @@ class FirebasePushProviderTest { pusherSubscriber: PusherSubscriber = FakePusherSubscriber(), isPlayServiceAvailable: IsPlayServiceAvailable = FakeIsPlayServiceAvailable(false), firebaseTokenRotator: FirebaseTokenRotator = FakeFirebaseTokenRotator(), + firebaseGatewayProvider: FirebaseGatewayProvider = FakeFirebaseGatewayProvider() ): FirebasePushProvider { return FirebasePushProvider( firebaseStore = firebaseStore, pusherSubscriber = pusherSubscriber, isPlayServiceAvailable = isPlayServiceAvailable, firebaseTokenRotator = firebaseTokenRotator, + firebaseGatewayProvider = firebaseGatewayProvider, ) } } diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index fd502263e9..1dec445c00 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -19,6 +19,7 @@ setupAnvil() dependencies { implementation(libs.dagger) + implementation(projects.features.enterprise.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) @@ -48,6 +49,8 @@ dependencies { testImplementation(libs.test.robolectric) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.kotlinx.collections.immutable) + testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultPushGatewayHttpUrlProvider.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultPushGatewayHttpUrlProvider.kt new file mode 100644 index 0000000000..af434c3020 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultPushGatewayHttpUrlProvider.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.pushproviders.unifiedpush + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.enterprise.api.EnterpriseService +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +interface DefaultPushGatewayHttpUrlProvider { + fun provide(): String +} + +@ContributesBinding(AppScope::class) +class DefaultDefaultPushGatewayHttpUrlProvider @Inject constructor( + private val enterpriseService: EnterpriseService, +) : DefaultPushGatewayHttpUrlProvider { + override fun provide(): String { + return enterpriseService.unifiedPushDefaultPushGateway() ?: UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL + } +} diff --git a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt index 918db35e4e..e7e31cfd67 100644 --- a/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt +++ b/libraries/pushproviders/unifiedpush/src/main/kotlin/io/element/android/libraries/pushproviders/unifiedpush/UnifiedPushGatewayUrlResolver.kt @@ -21,6 +21,7 @@ interface UnifiedPushGatewayUrlResolver { @ContributesBinding(AppScope::class) class DefaultUnifiedPushGatewayUrlResolver @Inject constructor( private val unifiedPushStore: UnifiedPushStore, + private val defaultPushGatewayHttpUrlProvider: DefaultPushGatewayHttpUrlProvider, ) : UnifiedPushGatewayUrlResolver { override fun resolve( gatewayResult: UnifiedPushGatewayResolverResult, @@ -33,7 +34,7 @@ class DefaultUnifiedPushGatewayUrlResolver @Inject constructor( } UnifiedPushGatewayResolverResult.ErrorInvalidUrl, UnifiedPushGatewayResolverResult.NoMatrixGateway -> { - UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL + defaultPushGatewayHttpUrlProvider.provide() } is UnifiedPushGatewayResolverResult.Success -> { gatewayResult.gateway diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-es/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..dca4f019be --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-es/translations.xml @@ -0,0 +1,10 @@ + + + "Asegurarse de que los distribuidores de UnifiedPush están disponibles." + "No se ha encontrado ningún distribuidor push." + + "%1$d distribuidor encontrado: %2$s." + "%1$d distribuidores encontrados: %2$s." + + "Verificar UnifiedPush" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-eu/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..7952c7d103 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-eu/translations.xml @@ -0,0 +1,9 @@ + + + "Ez da push banatzailerik aurkitu." + + "Banatzaile %1$d aurkitu da: %2$s." + "%1$d banatzailea aurkitu dira: %2$s." + + "Egiaztatu UnifiedPush" + diff --git a/libraries/pushproviders/unifiedpush/src/main/res/values-ka/translations.xml b/libraries/pushproviders/unifiedpush/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..b68a00704e --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/main/res/values-ka/translations.xml @@ -0,0 +1,10 @@ + + + "დარწმუნდით რომ UnifiedPush დისტრიბუტორები ხელმისაწვდომია." + "Push დისტრიბუტორები არ მოიძებნა." + + "%1$d დისტრიბუტორი მოიძებნა: %2$s" + "%1$d დისტრიბუტორი მოიძებნა: %2$s" + + "შეამოწმეთ UnifiedPush" + diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt index 7edc223f73..27008d96ec 100644 --- a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/DefaultUnifiedPushGatewayUrlResolverTest.kt @@ -18,7 +18,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest { gatewayResult = UnifiedPushGatewayResolverResult.ErrorInvalidUrl, instance = "", ) - assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) + assertThat(result).isEqualTo(A_UNIFIED_PUSH_GATEWAY) } @Test @@ -28,7 +28,7 @@ class DefaultUnifiedPushGatewayUrlResolverTest { gatewayResult = UnifiedPushGatewayResolverResult.NoMatrixGateway, instance = "", ) - assertThat(result).isEqualTo(UnifiedPushConfig.DEFAULT_PUSH_GATEWAY_HTTP_URL) + assertThat(result).isEqualTo(A_UNIFIED_PUSH_GATEWAY) } @Test @@ -77,7 +77,9 @@ class DefaultUnifiedPushGatewayUrlResolverTest { private fun createDefaultUnifiedPushGatewayUrlResolver( unifiedPushStore: UnifiedPushStore = FakeUnifiedPushStore(), + defaultPushGatewayHttpUrlProvider: DefaultPushGatewayHttpUrlProvider = FakeDefaultPushGatewayHttpUrlProvider(), ) = DefaultUnifiedPushGatewayUrlResolver( unifiedPushStore = unifiedPushStore, + defaultPushGatewayHttpUrlProvider = defaultPushGatewayHttpUrlProvider, ) } diff --git a/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeDefaultPushGatewayHttpUrlProvider.kt b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeDefaultPushGatewayHttpUrlProvider.kt new file mode 100644 index 0000000000..de0a293fc2 --- /dev/null +++ b/libraries/pushproviders/unifiedpush/src/test/kotlin/io/element/android/libraries/pushproviders/unifiedpush/FakeDefaultPushGatewayHttpUrlProvider.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.libraries.pushproviders.unifiedpush + +const val A_UNIFIED_PUSH_GATEWAY = "aGateway" + +class FakeDefaultPushGatewayHttpUrlProvider( + private val provideResult: () -> String = { A_UNIFIED_PUSH_GATEWAY } +) : DefaultPushGatewayHttpUrlProvider { + override fun provide(): String { + return provideResult() + } +} diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts index 22eb2338b6..4d21d63724 100644 --- a/libraries/session-storage/impl/build.gradle.kts +++ b/libraries/session-storage/impl/build.gradle.kts @@ -28,7 +28,6 @@ dependencies { implementation(libs.sqldelight.driver.android) implementation(libs.sqlcipher) implementation(libs.sqlite) - implementation(libs.androidx.security.crypto) implementation(projects.libraries.di) implementation(libs.sqldelight.coroutines) diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index ddc720ed40..8ee03dd5ad 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -97,6 +97,11 @@ object TestTags { */ val floatingActionButton = TestTag("floating-action-button") + /** + * Timeline. + */ + val timeline = TestTag("timeline") + /** * Timeline item. */ diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index a5d672ab5d..eeb6687a92 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -46,6 +46,7 @@ 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.Icon +import io.element.android.libraries.designsystem.theme.components.IconColorButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId @@ -54,8 +55,6 @@ import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetailsProvider import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag -import io.element.android.libraries.textcomposer.components.ComposerOptionsButton -import io.element.android.libraries.textcomposer.components.DismissTextFormattingButton import io.element.android.libraries.textcomposer.components.SendButton import io.element.android.libraries.textcomposer.components.TextFormatting import io.element.android.libraries.textcomposer.components.VoiceMessageDeleteButton @@ -141,10 +140,9 @@ fun TextComposer( Spacer(modifier = Modifier.width(16.dp)) } else -> { - ComposerOptionsButton( - modifier = Modifier - .size(48.dp), - onClick = onAddAttachment + IconColorButton( + onClick = onAddAttachment, + imageVector = CompoundIcons.Plus(), ) } } @@ -285,7 +283,10 @@ fun TextComposer( modifier = layoutModifier, textInput = textInput, dismissTextFormattingButton = { - DismissTextFormattingButton(onClick = onDismissTextFormatting) + IconColorButton( + onClick = onDismissTextFormatting, + imageVector = CompoundIcons.Close(), + ) }, textFormatting = textFormattingOptions, sendButton = sendButton, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt deleted file mode 100644 index c4b1350774..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/ComposerOptionsButton.kt +++ /dev/null @@ -1,46 +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.textcomposer.components - -import androidx.compose.foundation.layout.size -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.Icon -import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.textcomposer.R - -@Composable -internal fun ComposerOptionsButton( - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - IconButton( - modifier = modifier - .size(48.dp), - onClick = onClick - ) { - Icon( - modifier = Modifier.size(30.dp), - resourceId = CommonDrawables.ic_plus_composer, - contentDescription = stringResource(R.string.rich_text_editor_a11y_add_attachment), - tint = ElementTheme.colors.iconPrimary, - ) - } -} - -@PreviewsDayNight -@Composable -internal fun ComposerOptionsButtonPreview() = ElementPreview { - ComposerOptionsButton(onClick = {}) -} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt deleted file mode 100644 index 229cfc8275..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/DismissTextFormattingButton.kt +++ /dev/null @@ -1,46 +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.textcomposer.components - -import androidx.compose.foundation.layout.size -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.Icon -import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.ui.strings.CommonStrings - -@Composable -internal fun DismissTextFormattingButton( - onClick: () -> Unit, - modifier: Modifier = Modifier -) { - IconButton( - modifier = modifier - .size(48.dp), - onClick = onClick - ) { - Icon( - modifier = Modifier.size(30.dp), - resourceId = CommonDrawables.ic_cancel, - contentDescription = stringResource(CommonStrings.action_close), - tint = ElementTheme.colors.iconPrimary, - ) - } -} - -@PreviewsDayNight -@Composable -internal fun DismissTextFormattingButtonPreview() = ElementPreview { - DismissTextFormattingButton(onClick = {}) -} diff --git a/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml b/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml index 7eee4d830d..51c868112e 100644 --- a/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-cs/translations.xml @@ -7,6 +7,7 @@ "Volitelný titulek…" "Šifrovaná zpráva…" "Zpráva…" + "Nešifrovaná zpráva…" "Vytvořit odkaz" "Upravit odkaz" "Použít tučný text" diff --git a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml b/libraries/textcomposer/impl/src/main/res/values-de/translations.xml index 53ea09cfa8..f7636f4137 100644 --- a/libraries/textcomposer/impl/src/main/res/values-de/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-de/translations.xml @@ -5,7 +5,9 @@ "Formatierungsoptionen schließen" "Codeblock umschalten" "Optionale Bildunterschrift…" + "Verschlüsselte Nachricht…" "Nachricht…" + "Unverschlüsselte Nachricht" "Einen Link erstellen" "Link bearbeiten" "Fettes Format anwenden" diff --git a/libraries/textcomposer/impl/src/main/res/values-es/translations.xml b/libraries/textcomposer/impl/src/main/res/values-es/translations.xml index bc0db632ad..4102637763 100644 --- a/libraries/textcomposer/impl/src/main/res/values-es/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-es/translations.xml @@ -4,6 +4,8 @@ "Lista de puntos" "Cerrar opciones de formato" "Bloque de código" + "Agregar una leyenda" + "Mensaje cifrado…" "Mensaje…" "Crear un enlace" "Editar enlace" diff --git a/libraries/textcomposer/impl/src/main/res/values-et/translations.xml b/libraries/textcomposer/impl/src/main/res/values-et/translations.xml index e79d7c3e77..0fb7b98de5 100644 --- a/libraries/textcomposer/impl/src/main/res/values-et/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-et/translations.xml @@ -7,6 +7,7 @@ "Selgitus või nimi, kui soovid lisada…" "Krüptitud sõnum…" "Sõnum…" + "Krüptimata sõnum…" "Lisa link" "Muuda linki" "Kasuta paksu kirja" diff --git a/libraries/textcomposer/impl/src/main/res/values-eu/translations.xml b/libraries/textcomposer/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..c7d6a0863b --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,24 @@ + + + "Gehitu eranskina" + "Buleten zerrenda bai/ez" + "Itxi formatu aukerak" + "Gehitu testua" + "Mezua…" + "Sortu esteka" + "Editatu esteka" + "Aplikatu formatu lodia" + "Aplikatu formatu etzana" + "Aplikatu ezabaketa formatua" + "Aplikatu azpimarra formatua" + "Pantaila osoa bai/ez" + "Koska" + "Ezarri esteka" + "Zenbakidu zerrenda bai/ez" + "Ireki idazketa aukerak" + "Aipua bai/ez" + "Kendu esteka" + "Koskarik gabe" + "Esteka" + "Mantendu sakatuta grabatzeko" + diff --git a/libraries/textcomposer/impl/src/main/res/values-fi/translations.xml b/libraries/textcomposer/impl/src/main/res/values-fi/translations.xml index a7d2913937..473bd218f4 100644 --- a/libraries/textcomposer/impl/src/main/res/values-fi/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-fi/translations.xml @@ -5,7 +5,9 @@ "Sulje muotoiluasetukset" "Koodilohko päälle/pois" "Lisää kuvateksti" + "Salattu viesti…" "Viesti…" + "Salaamaton viesti…" "Luo linkki" "Muokkaa linkkiä" "Käytä lihavoitua muotoa" diff --git a/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml b/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml index 5f25e976cf..56f524e35f 100644 --- a/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-fr/translations.xml @@ -7,6 +7,7 @@ "Légende facultative…" "Message chiffré…" "Message…" + "Message non chiffré…" "Créer un lien" "Modifier le lien" "Appliquer le format gras" diff --git a/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml index 2b61cbd50f..28cfa42f12 100644 --- a/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-hu/translations.xml @@ -5,7 +5,9 @@ "Formázási beállítások bezárása" "Kódblokk be/ki" "Felirat hozzáadása…" + "Titkosított üzenet…" "Üzenet…" + "Titkosítatlan üzenet…" "Hivatkozás létrehozása" "Hivatkozás szerkesztése" "Félkövér formátum alkalmazása" diff --git a/libraries/textcomposer/impl/src/main/res/values-it/translations.xml b/libraries/textcomposer/impl/src/main/res/values-it/translations.xml index 04e45678eb..44eaee1fd3 100644 --- a/libraries/textcomposer/impl/src/main/res/values-it/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-it/translations.xml @@ -5,6 +5,7 @@ "Chiudi le opzioni di formattazione" "Attiva/disattiva il blocco di codice" "Aggiungi una didascalia" + "Messaggio cifrato…" "Messaggio…" "Crea un collegamento" "Modifica collegamento" diff --git a/libraries/textcomposer/impl/src/main/res/values-nb/translations.xml b/libraries/textcomposer/impl/src/main/res/values-nb/translations.xml index cc609ebee8..06a01dfcd9 100644 --- a/libraries/textcomposer/impl/src/main/res/values-nb/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-nb/translations.xml @@ -4,6 +4,7 @@ "Aktiver/deaktiver punktliste" "Lukk formateringsvalg" "Aktiver kodeblokk" + "Legg til en tekstbeskrivelse" "Melding…" "Opprett en lenke" "Rediger lenke" diff --git a/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml b/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml index c26355bf35..48ea1ad27a 100644 --- a/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-pt/translations.xml @@ -5,7 +5,9 @@ "Fechar opções de formatação" "Ativar/desativar bloco de código" "Legenda opcional…" + "Mensagem encriptada…" "Mensagem…" + "Mensagem não encriptada…" "Criar uma ligação" "Editar ligação" "Aplicar negrito" diff --git a/libraries/textcomposer/impl/src/main/res/values-sk/translations.xml b/libraries/textcomposer/impl/src/main/res/values-sk/translations.xml index 606064f786..fa2d3a11df 100644 --- a/libraries/textcomposer/impl/src/main/res/values-sk/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-sk/translations.xml @@ -7,6 +7,7 @@ "Voliteľný titulok…" "Šifrovaná správa…" "Správa…" + "Nešifrovaná správa…" "Vytvoriť odkaz" "Upraviť odkaz" "Použiť tučný formát" diff --git a/libraries/textcomposer/impl/src/main/res/values-sv/translations.xml b/libraries/textcomposer/impl/src/main/res/values-sv/translations.xml index ca402f750b..721812d60c 100644 --- a/libraries/textcomposer/impl/src/main/res/values-sv/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-sv/translations.xml @@ -5,7 +5,9 @@ "Stäng formateringsalternativ" "Växla kodblock" "Lägg till en bildtext" + "Krypterat meddelande …" "Meddelande …" + "Okrypterat meddelande …" "Skapa en länk" "Redigera länk" "Använd fetstil" diff --git a/libraries/textcomposer/impl/src/main/res/values-uk/translations.xml b/libraries/textcomposer/impl/src/main/res/values-uk/translations.xml index 9021625213..8eb5561bfc 100644 --- a/libraries/textcomposer/impl/src/main/res/values-uk/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-uk/translations.xml @@ -5,7 +5,9 @@ "Закрити параметри форматування" "Перемкнути блок коду" "Необов\'язковий підпис…" + "Зашифроване повідомлення…" "Повідомлення…" + "Незашифроване повідомлення…" "Створити посилання" "Редагувати посилання" "Жирний формат" diff --git a/libraries/textcomposer/impl/src/main/res/values/localazy.xml b/libraries/textcomposer/impl/src/main/res/values/localazy.xml index 4f6ea6651f..d79da4a10f 100644 --- a/libraries/textcomposer/impl/src/main/res/values/localazy.xml +++ b/libraries/textcomposer/impl/src/main/res/values/localazy.xml @@ -7,6 +7,7 @@ "Add a caption" "Encrypted message…" "Message…" + "Unencrypted message…" "Create a link" "Edit link" "Apply bold format" diff --git a/libraries/troubleshoot/impl/src/main/res/values-es/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..0ce2686562 --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-es/translations.xml @@ -0,0 +1,11 @@ + + + "Ejecutar pruebas" + "Volver a ejecutar pruebas" + "Algunas pruebas fallaron. Por favor, verifica los detalles." + "Ejecuta las pruebas para detectar cualquier problema en tu configuración que pueda hacer que las notificaciones no se comporten como es debido." + "Intentar arreglar" + "Todas las pruebas se han superado con éxito." + "Solucionar problemas con las notificaciones" + "Algunas pruebas requieren tu atención. Por favor, verifica los detalles." + diff --git a/libraries/troubleshoot/impl/src/main/res/values-eu/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..f609e427c2 --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-eu/translations.xml @@ -0,0 +1,9 @@ + + + "Exekutatu testak" + "Egin probak berriro" + "Proba batzuek huts egin dute. Aztertu xehetasunak." + "Saiatu konpontzen" + "Proba guztiak arrakastaz gainditu dira." + "Proba batzuek zure arreta eskatzen dute. Aztertu xehetasunak." + diff --git a/libraries/troubleshoot/impl/src/main/res/values-ka/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-ka/translations.xml new file mode 100644 index 0000000000..347301a776 --- /dev/null +++ b/libraries/troubleshoot/impl/src/main/res/values-ka/translations.xml @@ -0,0 +1,11 @@ + + + "ტესტების გაშვება" + "ტესტების ისევ გაშვება" + "ზოგიერთი ტესტი წარუმატებელია. გთხოვთ შეამოწმოთ დეტალები." + "ჩაატარეთ ტესტები თქვენს კონფიგურაციაში იმ პრობლემების აღმოსაჩენად, რომლებიც გამოიწვევენ შეტყობინებების არასწორ ქცევას." + "შეკეთების მცდელობა" + "ყველა ტესტი წარმატებით დასრულდა." + "პრობლემების გადაჭრის შეტყობინებები" + "ზოგიერთი ტესტი ითხოვს თქვენს ყურადღებას. გთხოვთ შეამოწმოთ დეტალები." + diff --git a/libraries/troubleshoot/impl/src/main/res/values-nb/translations.xml b/libraries/troubleshoot/impl/src/main/res/values-nb/translations.xml index fdd8ac10f9..7d69570f4c 100644 --- a/libraries/troubleshoot/impl/src/main/res/values-nb/translations.xml +++ b/libraries/troubleshoot/impl/src/main/res/values-nb/translations.xml @@ -1,5 +1,11 @@ + "Kjør tester" + "Kjør tester igjen" + "Noen tester mislyktes. Vennligst sjekk detaljene." + "Kjør testene for å avdekke eventuelle problemer i konfigurasjonen som kan føre til at varslene ikke oppfører seg som forventet." "Forsøk å fikse" "Alle testene ble bestått." + "Feilsøk varsler" + "Noen tester krever din oppmerksomhet. Vennligst sjekk detaljene." diff --git a/libraries/ui-strings/build.gradle.kts b/libraries/ui-strings/build.gradle.kts index 2f6a51497d..7e34891c66 100644 --- a/libraries/ui-strings/build.gradle.kts +++ b/libraries/ui-strings/build.gradle.kts @@ -11,4 +11,8 @@ plugins { android { namespace = "io.element.android.libraries.ui.strings" + + lint { + disable += "Typos" + } } 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 348fd40389..bd95245924 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -147,7 +147,9 @@ "Zkopírováno do schránky" "Autorská práva" "Vytváření místnosti…" + "Žádost zrušena" "Místnost opuštěna" + "Pozvánka odmítnuta" "Tmavé" "Chyba dešifrování" "Možnosti pro vývojáře" 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 6a8ecc719b..fa2cb5025c 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -145,7 +145,9 @@ "In die Zwischenablage kopiert" "Copyright" "Raum wird erstellt…" + "Anfrage storniert" "Hat den Raum verlassen" + "Einladung abgelehnt" "Dunkel" "Dekodierungsfehler" "Entwickleroptionen" @@ -339,6 +341,7 @@ Grund: %1$s." "Ihre Nachricht wurde nicht geschickt, da Sie eines oder mehrere Ihrer Geräte nicht verifiziert haben." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Benutzerdetails konnten nicht abgerufen werden" + "Nachricht in %1$s" "%1$s von %2$s" "%1$s fixierte Nachrichten" "Nachricht wird geladen…" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 688c36348d..66ffa2aee3 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -33,9 +33,12 @@ "Grabar mensaje de voz" "Detener grabación" "Aceptar" + "Agregar leyenda" "Añadir a la cronología" "Atrás" + "Llamar" "Cancelar" + "Cancelar por ahora" "Elegir foto" "Borrar" "Cerrar" @@ -44,17 +47,21 @@ "Confirmar contraseña" "Continuar" "Copiar" + "Copiar leyenda" "Copiar enlace" "Copiar enlace al mensaje" "Copiar texto" "Crear" "Crear una sala" + "Desactivar" + "Desactivar cuenta" "Rechazar" "Eliminar encuesta" "Desactivar" "Descartar" "Hecho" "Editar" + "Editar leyenda" "Editar encuesta" "Activar" "Finalizar encuesta" @@ -76,21 +83,27 @@ "Cargar más" "Gestionar cuenta" "Administrar dispositivos" + "Enviar mensaje" "Siguiente" "No" "Ahora no" "OK" "Ajustes" "Abrir con" + "Fijar" "Respuesta rápida" "Citar" "Reaccionar" + "Rechazar" "Eliminar" + "Eliminar leyenda" + "Eliminar mensaje" "Responder" "Responder en el hilo" "Informar de un error" - "Reportar Contenido" + "Reportar contenido" "Restablecer" + "Restablecer identidad" "Reintentar" "Reintentar descifrado" "Guardar" @@ -99,6 +112,7 @@ "Enviar mensaje" "Compartir" "Compartir enlace" + "Mostrar" "Inicia sesión de nuevo" "Cerrar sesión" "Cerrar sesión de todos modos" @@ -109,38 +123,58 @@ "Pulsa para cargar el mapa" "Hacer foto" "Toca para ver opciones" - "Inténtalo de nuevo" - "Ver Fuente" + "Intentar de nuevo" + "Desprender" + "Ver en la línea de tiempo" + "Ver fuente" "Sí" + "Sí, intentar de nuevo" + "Tu servidor es ahora compatible con un protocolo nuevo y más rápido. Cierra la sesión y vuelve a iniciarla para actualizar ahora. Haciéndolo ahora, evitarás tener que cerrar la sesión cuando se elimine el protocolo antiguo." + "Actualización disponible" "Acerca de" "Política de uso aceptable" + "Añadiendo leyenda" "Ajustes avanzados" "Estadísticas" "Apariencia" "Sonido" "Usuarios bloqueados" "Burbujas" + "Llamada iniciada" "Copia de seguridad del chat" + "Copiado al portapapeles" "Derechos de autor" "Creando sala…" "Saliste de la sala" "Oscuro" "Error de descifrado" "Opciones de desarrollador" + "ID de dispositivo" "Chat directo" + "No mostrar de nuevo" + "Descarga fallida" + "Descargando" "(editado)" "Edición" + "Editando leyenda" "* %1$s %2$s" + "Cifrado" "Cifrado activado" "Introduce tu PIN" "Error" + "Se ha producido un error, es posible que no recibas notificaciones de mensajes nuevos. Soluciona los problemas con las notificaciones desde los ajustes. + +Motivo: %1$s." "Todos" "Falló" "Favorito" "Marcado como favorito" "Archivo" + "Archivo eliminado" + "Archivo guardado" "Archivo guardado en Descargas" "Reenviar mensaje" + "Usado frecuentemente" "GIF" "Imagen" "En respuesta a %1$s" @@ -150,6 +184,7 @@ "Claro" "Enlace copiado al portapapeles" "Cargando…" + "Cargando más…" "%1$d miembro" "%1$d miembros" @@ -160,13 +195,18 @@ "Mensaje eliminado" "Moderno" "Silenciar" + "%1$s (%2$s)" "No hay resultados" + "Sala sin nombre" "Sin conexión" + "Licencias de código abierto" "o" "Contraseña" "Personas" "Enlace permanente" "Permiso" + "Fijado" + "Espera, por favor…" "¿Estás seguro de que quieres finalizar esta encuesta?" "Encuesta: %1$s" "Total de votos: %1$s" @@ -195,6 +235,7 @@ "Buscar resultados" "Seguridad" "Visto por" + "Enviar a" "Enviando…" "Fallo al enviar" "Enviado" @@ -203,6 +244,7 @@ "Ajustes" "Ubicación compartida" "Cerrando sesión" + "Algo salió mal" "Iniciando chat…" "Sticker" "Terminado" @@ -216,51 +258,109 @@ "¿De qué trata esta sala?" "No se puede descifrar" "Enviado desde un dispositivo no seguro" + "No tienes acceso a este mensaje" + "La identidad verificada del remitente ha cambiado" "Las invitaciones no se pudieron enviar a uno o más usuarios." "No se pudo enviar la(s) invitación(es)" "Desbloquear" "Dejar de silenciar" + "Llamada no compatible" "Evento no compatible" "Usuario" "Verificación cancelada" "Verificación completada" + "Verificación fallida" + "Verificado" "Verificar dispositivo" + "Verificar identidad" + "Verificar usuario" "Vídeo" "Mensaje de voz" "Esperando…" "Esperando este mensaje" + "Tú" + "La identidad de %1$s parece haber cambiado. %2$s" + "La identidad %2$s de %1$s parece haber cambiado. %3$s" + "(%1$s)" + "La identidad verificada de %1$s ha cambiado." + "La identidad verificada %2$s de %1$s ha cambiado. %3$s" + "Retirar la verificación" "Confirmar" "Error" "Terminado" "Atención" "Tus cambios no se han guardado. ¿Estás seguro de que quieres volver atrás?" "¿Guardar cambios?" + "Tu servidor base debe actualizarse para admitir Matrix Authentication Service y la creación de cuentas." "No se pudo crear el enlace permanente" "%1$s no pudo cargar el mapa. Por favor vuelve a intentarlo más tarde." "Error al cargar mensajes" "%1$s no ha podido acceder a tu ubicación. Por favor vuelve a intentarlo más tarde." "No se pudo cargar tu mensaje de voz." + "Mensaje no encontrado" "%1$s no tiene permiso para acceder a tu ubicación. Puedes habilitar el acceso en Ajustes." "%1$s no tiene permiso para acceder a tu ubicación. Habilita el acceso a continuación." "%1$s no tiene permiso para acceder al micrófono. Habilita el acceso para grabar un mensaje de voz." + "Esto puede deberse a problemas de red o del servidor." + "Esta dirección de sala ya existe. Intenta editar el campo de dirección de sala o cambia el nombre de la sala" + "No se permiten algunos caracteres. Solo se admiten letras, dígitos y los siguientes símbolos ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" "Algunos mensajes no se han enviado" "Lo siento, se ha producido un error" + "La autenticidad de este mensaje cifrado no puede ser garantizada en este dispositivo." + "Cifrado por un usuario verificado anteriormente." + "No cifrado." + "Cifrado por un dispositivo desconocido o eliminado." + "Cifrado por un dispositivo no verificado por su propietario." + "Cifrado por un usuario no verificado." "🔐️ Únete a mí en %1$s" "Hola, puedes hablar conmigo en %1$s: %2$s" "%1$s Android" "Agitar con fuerza para informar de un error" "Error al seleccionar archivos multimedia, por favor inténtalo de nuevo." + "Es posible que las leyendas no sean visibles para las personas que usan aplicaciones más antiguas." "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "Error al subir el contenido multimedia, por favor inténtalo de nuevo." + "Presiona sobre un mensaje y selecciona «%1$s» para incluirlo aquí." + "Fija los mensajes importantes para que se puedan descubrir fácilmente" + + "%1$d mensaje fijado" + "%1$d mensajes fijados" + + "Mensajes fijados" + "Estás a punto de ir a tu cuenta de %1$s para restablecer tu identidad. Posteriormente volverás a la aplicación." + "¿No puedes confirmar? Ve a tu cuenta para restablecer tu identidad." + "Retirar la verificación y enviar" + "Puedes retirar tu verificación y enviar este mensaje de todos modos, o puedes cancelarlo por ahora e intentarlo de nuevo más tarde después de volver a verificar a %1$s." + "Tu mensaje no se envió porque la identidad verificada de %1$s ha cambiado" + "Enviar mensaje de todos modos" + "%1$s utiliza uno o más dispositivos no verificados. Puedes enviar el mensaje de todos modos, o puedes cancelarlo por ahora y volver a intentarlo más tarde, una vez %2$s haya verificado todos sus dispositivos." + "Tu mensaje no se envió porque %1$s no ha verificado todos los dispositivos" + "Uno o más de tus dispositivos no están verificados. Puedes enviar el mensaje de todos modos, o puedes cancelarlo por ahora e intentarlo de nuevo más tarde, una vez hayas verificado todos tus dispositivos." + "Tu mensaje no se envió porque no has verificado uno o más de tus dispositivos" "Error al procesar el contenido multimedia, por favor inténtalo de nuevo." "No se pudieron recuperar los detalles del usuario" + "Mensaje en %1$s" + "%1$s de %2$s" + "%1$s mensajes fijados" + "Cargando mensaje…" + "Ver todos" + "Chat" + "Solicitud de unión enviada" "Compartir ubicación" "Compartir mi ubicación" "Abrir en Apple Maps" "Abrir en Google Maps" "Abrir en OpenStreetMap" "Compartir esta ubicación" + "Mensaje no enviado porque la identidad verificada de %1$s ha cambiado." + "Mensaje no enviado porque %1$s no ha verificado todos los dispositivos." + "Mensaje no enviado porque no has verificado uno o más de tus dispositivos." "Ubicación" "Versión: %1$s (%2$s)" "es" + "Los mensajes históricos no están disponibles en este dispositivo" + "Debes verificar este dispositivo para acceder a los mensajes históricos" + "No tienes acceso a este mensaje" + "No se ha podido descifrar el mensaje" + "Este mensaje fue bloqueado bien sea porque no verificaste tu dispositivo o porque el remitente necesita verificar tu identidad." 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 be6df95563..8328eadc3a 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -30,7 +30,7 @@ "Helista" "Kasutajamenüü" "Vaata üksikasju" - "Salvesta häälsõnum" + "Salvesta häälsõnum." "Lõpeta salvestamine" "Nõustu" "Lisa selgitus" @@ -145,7 +145,9 @@ "Kopeeritud lõikelauale" "Autoriõigused" "Loome jututoa…" + "Päring on tühistatud" "Lahkus jututoast" + "Keeldusid kutsest" "Tume" "Dekrüptimisviga" "Arendaja valikud" @@ -285,6 +287,10 @@ Põhjus: %1$s." "%1$s kasutaja verifitseeritud identiteet on muutunud." "%1$s kasutaja (%2$s kasutajanimi) verifitseeritud identiteet on muutunud. %3$s" "Võta verifitseerimine tagasi" + "%1$s link viib sind teise veebisaiti %2$s + +Kas sa oled kindel, et soovid jätkata?" + "Palun kontrolli seda linki mõttega" "Kinnitus" "Viga" "Õnnestus" diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml new file mode 100644 index 0000000000..86cff64109 --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -0,0 +1,305 @@ + + + "Ezabatu" + "Ezkutatu pasahitza" + "Joan behera" + "Aipamenak soilik" + "Mutututa" + "%1$d. orria" + "Eten" + "PINaren eremua" + "Erreproduzitu" + "Inkesta" + "Amaitutako inkesta" + "Erreakzionatu %1$s(e)kin" + "Erreakzionatu beste emoji batzuekin" + "%1$s(e)k eta %2$s(e)k irakurri dute" + + "%1$s(e)k eta beste %2$dek irakurri dute" + "%1$s(e)k eta beste %2$d(e)k irakurri dute" + + "%1$s(e)k irakurri du" + "Egin tap guztiak ikusteko" + "Kendu %1$s erreakzioa" + "Bidali fitxategiak" + "Erakutsi pasahitza" + "Hasi dei bat" + "Erabiltzailea-menua" + "Ikusi xehetasunak" + "Grabatu ahots-mezua." + "Utzi grabatzeari" + "Onartu" + "Gehitu testua" + "Gehitu denbora-lerrora" + "Atzera" + "Deia" + "Utzi" + "Utzi oraingoz" + "Aukeratu argazkia" + "Garbitu" + "Itxi" + "Burutu egiaztapena" + "Berretsi" + "Berretsi pasahitza" + "Jarraitu" + "Kopiatu" + "Kopiatu testua" + "Kopiatu esteka" + "Kopiatu esteka mezura" + "Kopiatu testua" + "Sortu" + "Sortu gela" + "Desaktibatu" + "Desaktibatu kontua" + "Ukatu" + "Ezabatu inkesta" + "Desgaitu" + "Baztertu" + "Eginda" + "Editatu" + "Editatu testua" + "Editatu inkesta" + "Gaitu" + "Amaitu inkesta" + "Sartu PINa" + "Pasahitza ahaztu duzu?" + "Birbidali" + "Joan atzera" + "Ezikusi" + "Gonbidatu" + "Gonbidatu jendea" + "Gonbidatu jendea %1$s(e)ra" + "Gonbidatu jendea %1$s(e)ra" + "Gonbidapenak" + "Elkartu" + "Informazio gehiago" + "Atera" + "Utzi elkarrizketa" + "Atera gelatik" + "Kargatu gehiago" + "Kudeatu kontua" + "Kudeatu gailuak" + "Bidali mezua" + "Hurrengoa" + "Ez" + "Orain ez" + "Ados" + "Ezarpenak" + "Ireki honekin…" + "Finkatu" + "Erantzun azkarra" + "Aipatu" + "Erreakzioa" + "Baztertu" + "Kendu" + "Kendu testua" + "Kendu mezua" + "Erantzun" + "Erantzun harian" + "Eman errore baten berri" + "Salatu edukia" + "Berrezarri" + "Berrezarri identitatea" + "Saiatu berriro" + "Saiatu berriro deszifratzen" + "Gorde" + "Bilatu" + "Bidali" + "Bidali mezua" + "Partekatu" + "Partekatu esteka" + "Erakutsi" + "Hasi saioa berriro" + "Amaitu saioa" + "Amaitu saioa edonola ere" + "Saltatu" + "Hasi" + "Hasi txata" + "Hasi egiaztapena" + "Sakatu mapa kargatzeko" + "Egin argazkia" + "Sakatu aukerak ikusteko" + "Saiatu berriro" + "Utzi finkatzeari" + "Ikusi denbora-lerroan" + "Ikusi iturburua" + "Bai" + "Bai, saiatu berriro" + "Honi buruz" + "Testua gehitzen" + "Ezarpen aurreratuak" + "Estatistikak" + "Itxura" + "Audioa" + "Blokeatutako erabiltzaileak" + "Bunbuiloak" + "Deia hasi da" + "Txataren babeskopia" + "Arbelean kopiatu da" + "Copyrighta" + "Gela sortzen…" + "Gelatik atera da" + "Iluna" + "Deszifratze-errorea" + "Garapen aukerak" + "Gailuaren IDa" + "Txata zuzena" + "Ez erakutsi berriro" + "Deskargatzen" + "(Editatua)" + "Editatzen" + "Testua editatzen" + "Zifratzea" + "Zifratzea gaituta" + "Sartu zure PINa" + "Errorea" + "Errore bat gertatu da, litekeena da mezu berrien jakinarazpenik ez jasotzea. Aztertu jakinarazpenen ezarpenak. + +Arrazoia: %1$s." + "Guztiak" + "Huts egin du" + "Gogokoa" + "Gogoko eginda" + "Fitxategia" + "Fitxategia ezabatu da" + "Fitxategia gorde da" + "Fitxategia Deskargak atalean gorde da" + "Birbidali mezua" + "Maiz erabilitakoa" + "GIFa" + "Irudia" + "%1$s(r)i erantzunez" + "Instalatu APK" + "Matrix IDa ezin da topatu eta, beraz, litekeena da gonbidapena ez jasotzea." + "Gelatik ateratzen" + "Argia" + "Esteka arbelean kopiatu da" + "Kargatzen…" + + "Kide %1$d" + "%1$d kide" + + "Mezua" + "Mezuen antolaketa" + "Mezua kendu da" + "Modernoa" + "Mututu" + "Emaitzarik ez" + "Gelak ez du izenik" + "Deskonektatuta" + "Kode irekiko lizentziak" + "edo" + "Pasahitza" + "Jendea" + "Esteka iraunkorra" + "Baimena" + "Finkatuta" + "Itxaron…" + "Ziur inkesta hau amaitu nahi duzula?" + "Inkesta:%1$s" + "Boto guztira: %1$s" + "Emaitzak inkesta amaitu ondoren erakutsiko dira" + + "Boto %d" + "%d boto" + + "Pribatutasun-politika" + "Erreakzioa" + "Erreakzioak" + "Berreskuratze-gakoa" + "Freskatzen…" + "%1$s(r)i erantzuten" + "Eman akats baten berri" + "Eman arazo baten berri" + "Salaketa bidali da" + "Testu aberatsaren editorea" + "Gela" + "Gelaren izena" + "adibidez, zure proiektuaren izena" + "Gordetako aldaketak" + "Gordetzen" + "Blokeo-pantaila" + "Bilatu norbait" + "Bilaketaren emaitzak" + "Segurtasuna" + "Ikusi du" + "Bidali" + "Bidaltzen…" + "Bidalita" + "Zerbitzaria ez da bateragarria" + "Zerbitzariaren URLa" + "Ezarpenak" + "Partekatutako kokapena" + "Saioa amaitzen" + "Arazoren bat egon da" + "Txata hasten…" + "Pegatina" + "Arrakasta" + "Iradokizunak" + "Sinkronizatzen" + "Sistema" + "Testua" + "Hirugarrenei buruzko oharrak" + "Haria" + "Gaia" + "Zeri buruzko gela da?" + "Ezin da desenkriptatu" + "Ezin izan dira gonbidapenak erabiltzaile bati edo gehiagori bidali." + "Ezin d(ir)a gonbidapena(k) bidali" + "Desblokeatu" + "Aktibatu audioa" + "Deia ez da bateragarria" + "Gertaera ez da bateragarria" + "Erabiltzaile-izena" + "Egiaztaketa ezeztatuta" + "Egiaztaketa burututa" + "Egiaztapenak huts egin du" + "Egiaztatu gailua" + "Egiaztatu identitatea" + "Bideoa" + "Ahots-mezua" + "Zain…" + "Mezu honen zain" + "Zeu" + "%1$s(r)en identitatea aldatu dela dirudi. %2$s" + "Baieztapena" + "Errorea" + "Arrakasta" + "Abisua" + "Zure aldaketak ez dira gorde. Ziur itzuli nahi duzula?" + "Aldaketak gorde?" + "Huts egin du esteka iraunkorra sortzeak" + "%1$s ezin izan da mapa kargatu. Saiatu berriro geroago." + "Huts egin du mezuak kargatzeak" + "%1$s ezin izan da zure kokapena atzitu. Saiatu berriro geroago." + "Ez da mezua aurkitu" + "%1$sek ez du zure kokapena atzitzeko baimenik. Sarbidea Ezarpenetan gaitu dezakezu." + "%1$sek ez du zure kokapena atzitzeko baimenik. Gaitu sarbidea behean." + "%1$s(e)k ez du mikrofonoa atzitzeko baimenik. Gaitu sarbidea ahots-mezua grabatzeko." + "Gela badago lehendik ere. Saiatu gelaren helbidearen eremua editatzen edo aldatu gelaren izena" + "Karaktere batzuk ez daude baimenduta. Hizkiak, zifrak, eta ondorengo ikurrak onartzen dira bakarrik: ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" + "Mezu batzuk ez dira bidali" + "Barka, errore bat gertatu da" + "Ez dago zifratuta." + "🔐️ Zatoz nirekin %1$s(e)ra" + "%1$s Android" + "Astindu erroreen berri emateko" + "Huts egin du multimedia aukeratzeak, saiatu berriro." + "Huts egin du multimedia igotzeak, saiatu berriro." + "Finkatutako mezuak" + "Bidali mezua hala ere" + "%2$s(e)tik %1$s" + "Mezua kargatzen…" + "Ikusi guztia" + "Txata" + "Sartzeko eskaera bidali da" + "Partekatu kokapena" + "Partekatu nire kokapena" + "Ireki Apple Maps-en" + "Ireki Google Maps-en" + "Ireki OpenStreetMap-en" + "Partekatu kokapen hau" + "Kokapena" + "Bertsioa: %1$s (%2$s)" + "eu" + 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 2528bab353..290d34fee8 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -145,7 +145,9 @@ "Kopioitu leikepöydälle" "Tekijänoikeudet" "Luodaan huonetta…" + "Pyyntö peruutettu" "Poistuit huoneesta" + "Kutsu hylätty" "Tumma" "Salauksen purkuvirhe" "Kehittäjän asetukset" @@ -195,6 +197,7 @@ Syy: %1$s." "Viesti poistettu" "Moderni" "Mykistä" + "%1$s (%2$s)" "Ei tuloksia" "Nimetön huone" "Ei yhteyttä" @@ -258,7 +261,7 @@ Syy: %1$s." "Salauksen purkaminen ei onnistunut" "Lähetetty suojaamattomasta laitteesta" "Sinulla ei ole oikeutta lukea tätä viestiä" - "Lähettäjän vahvistettu identiteetti on vaihtunut" + "Lähettäjän vahvistettu identiteetti on muuttunut" "Kutsujen ei voitu lähettää yhdelle tai useammalle käyttäjälle." "Kutsujen lähettäminen ei onnistunut" "Avaa" @@ -272,6 +275,7 @@ Syy: %1$s." "Vahvistettu" "Vahvista laite" "Vahvista identiteetti" + "Vahvista käyttäjä" "Video" "Ääniviesti" "Odotetaan…" @@ -280,6 +284,7 @@ Syy: %1$s." "Käyttäjän %1$s identiteetti näyttää muuttuneen. %2$s" "Käyttäjän %1$s %2$s identiteetti näyttää muuttuneen. %3$s" "(%1$s)" + "Käyttäjän %1$s vahvistettu identiteetti on muuttunut." "Käyttäjän %1$s %2$s vahvistettu identiteetti on muuttunut. %3$s" "Peruuta vahvistus" "Vahvistus" @@ -298,6 +303,7 @@ Syy: %1$s." "%1$s -sovelluksella ei ole lupaa sijaintiisi. Voit sallia sen asetuksista." "%1$s -sovelluksella ei ole lupaa sijaintiisi. Voit sallia sen painamalla alla olevaa nappia." "%1$s -sovelluksella ei ole lupaa käyttää mikrofoniasi. Anna lupa, jotta voit nauhoittaa ääniviestejä." + "Tämä voi johtua verkko- tai palvelinongelmista." "Tämä huoneen osoite on jo käytössä, yritä muokata huoneen osoitekenttää tai muuta huoneen nimeä" "Jotkin merkit eivät ole sallittuja. Vain kirjaimet, numerot ja seuraavat symbolit ovat tuettuja ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" "Joitakin viestejä ei ole lähetetty" @@ -335,6 +341,7 @@ Syy: %1$s." "Viestiäsi ei lähetetty, koska et ole vahvistanut yhtä tai useampaa laitettasi." "Median käsittely epäonnistui, yritä uudelleen." "Käyttäjän tietojen hakeminen epäonnistui" + "Viesti huoneessa %1$s" "%1$s / %2$s" "Kiinnitetty viesti %1$s" "Viestiä ladataan…" 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 c35bf04061..2f82dee6e0 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -16,7 +16,7 @@ "Sondage" "Sondage terminé" "Réagir avec %1$s" - "Réagir avec d’autres emojis" + "Réagir avec d’autres émojis" "Lu par %1$s et %2$s" "Lu par %1$s et %2$d autre" @@ -145,7 +145,9 @@ "Copié dans le presse-papiers" "Droits d’auteur" "Création du salon…" + "Demande annulée" "Quitter le salon" + "Invitation refusée" "Sombre" "Erreur de déchiffrement" "Options pour les développeurs" @@ -285,6 +287,10 @@ Raison : %1$s." "L’identité vérifiée de l’utilisateur %1$s a changé." "L’identité vérifiée de %1$s %2$s a changé. %3$s" "Révoquer la vérification" + "Le lien \"%1$s\" vous redirige vers un autre site \"%2$s\". + +Êtes-vous sûr de vouloir continuer ?" + "Veuillez vérifier ce lien" "Confirmation" "Erreur" "Succès" 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 66d8f44fbb..34f216b6b5 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -145,7 +145,9 @@ "A vágólapra másolva" "Szerzői jogok" "Szoba létrehozása…" + "Kérés megszakítva" "Elhagyta a szobát" + "Meghívás elutasítva" "Sötét" "Visszafejtési hiba" "Fejlesztői beállítások" @@ -339,6 +341,7 @@ Ok: %1$s." "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte" "Nem sikerült feldolgozni a feltöltendő médiát, próbálja újra." "Nem sikerült letölteni a felhasználói adatokat" + "Üzenet a következőben: %1$s" "%1$s / %2$s" "%1$s kitűzött üzenet" "Üzenet betöltése…" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 5b97172682..933bb0c32d 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -195,6 +195,7 @@ Motivo:. %1$s" "Messaggio rimosso" "Moderno" "Silenzia" + "%1$s (%2$s)" "Nessun risultato" "Nessun nome della stanza" "Non in linea" @@ -272,6 +273,7 @@ Motivo:. %1$s" "Verificato" "Verifica dispositivo" "Verifica l\'identità" + "Verifica utente" "Video" "Messaggio vocale" "In attesa…" @@ -280,6 +282,7 @@ Motivo:. %1$s" "L\'identità di %1$s sembra essere cambiata. %2$s" "L\'identità di %1$s %2$s sembra essere cambiata. %3$s" "(%1$s)" + "L\'identità verificata di %1$s è cambiata." "L\'identità %2$s verificata di %1$s è cambiata. %3$s" "Ritira verifica" "Conferma" @@ -298,6 +301,7 @@ Motivo:. %1$s" "%1$s non ha l\'autorizzazione di accedere alla tua posizione. Puoi attivare l\'accesso nelle impostazioni." "%1$s non ha l\'autorizzazione per accedere alla tua posizione. Attiva l\'accesso di seguito." "%1$s non ha l\'autorizzazione di accedere al microfono. Attiva l\'accesso per registrare un messaggio vocale." + "Ciò può essere dovuto a problemi di rete o server." "L\'indirizzo di questa stanza esiste già. Prova a modificare il campo dell\'indirizzo o a cambiare il nome della stanza" "Alcuni caratteri non sono consentiti. Sono supportate solo lettere, cifre e i seguenti simboli ! $ & \'() * +/; =? @ [] - . _" "Alcuni messaggi non sono stati inviati" @@ -335,6 +339,7 @@ Motivo:. %1$s" "Il tuo messaggio non è stato inviato perché non hai verificato uno o più dispositivi." "Elaborazione del file multimediale da caricare fallita, riprova." "Impossibile recuperare i dettagli dell\'utente" + "Messaggio in %1$s" "%1$s di %2$s" "%1$s Messaggi fissati" "Caricamento messaggio…" diff --git a/libraries/ui-strings/src/main/res/values-ka/translations.xml b/libraries/ui-strings/src/main/res/values-ka/translations.xml index 0df6e83bef..88bd672c82 100644 --- a/libraries/ui-strings/src/main/res/values-ka/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ka/translations.xml @@ -29,6 +29,7 @@ "პაროლის ჩვენება" "დარეკვა" "მომხმარებლის მენიუ" + "დეტალების ნახვა" "ხმოვანი შეტყობინების ჩაწერა." "ჩაწერის შეწყვეტა" "მიღება" @@ -49,6 +50,7 @@ "უარყოფა" "გამოკითხვის წაშლა" "გამორთვა" + "გაუქმება" "მზადაა" "რედაქტირება" "გამოკითხვის რედაქტირება" @@ -57,6 +59,7 @@ "შეიყვანეთ PIN" "დაგავიწყდათ პაროლი?" "გადაგზავნა" + "დაბრუნება" "მოწვევა" "ხალხის მოწვევა" "ადამიანების დამატება %1$s" @@ -65,6 +68,7 @@ "გაწევრიანება" "შეიტყვეთ მეტი" "დატოვება" + "საუბრის დატოვება" "ოთახის დატოვება" "მეტის ჩატვირთვა" "ანგარიშის მართვა" @@ -83,6 +87,7 @@ "პასუხი თემაში" "ხარვეზის შეტყობინება" "კონტენტის რეპორტი" + "განულება" "ხელახლა ცდა" "გაშიფვრის ხელახლა ცდა" "შენახვა" @@ -110,6 +115,7 @@ "ანალიტიკა" "გარეგნობა" "აუდიო" + "დაბლოკილი მომხმარებლები" "ბუშტები" "ჩატის სარეზერვო ასლი" "საავტორო უფლება" @@ -126,6 +132,9 @@ "შეიყვანეთ თქვენი PIN" "შეცდომა" "ყველა" + "ვერ შესრულდა" + "რჩეული" + "რჩეულები" "ფაილი" "ფაილი შენახულია ჩამოტვირთვებში" "შეტყობინების გადაგზავნა" @@ -150,6 +159,7 @@ "დადუმება" "შედეგი არ არის" "ხაზგარეშე" + "ან" "პაროლი" "ხალხი" "მუდმივი ბმული" @@ -177,6 +187,8 @@ "ოთახი" "ოთახის სახელი" "მაგ. თქვენი პროექტის სახელი" + "შენახული ცვლილებები" + "შენახვა" "ეკრანის დაბლოკვა" "ვიღაცის ძებნა" "ძიების შედეგები" @@ -219,6 +231,8 @@ "შეცდომა" "წარმატება" "გაფრთხილება" + "თქვენი ცვლილებები არაა შენახული. დარწმუნებული ხართ დაბრუნებაში?" + "შენახვა?" "მუდმივი ბმულის შექმნა ვერ მოხერხდა" "ვერ გამოვიდა რუკის %1$s ჩატვირთვა. გთხოვთ, მოგვიანებით სცადოთ." "შეტყობინებების ჩატვირთვა ვერ მოხერხდა" 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 cca77d0afc..7d4e65b5ac 100644 --- a/libraries/ui-strings/src/main/res/values-nb/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nb/translations.xml @@ -1,6 +1,10 @@ "Slett" + + "%1$d siffer angitt" + "%1$d sifre angitt" + "Skjul passord" "Hopp til bunnen" "Bare omtaler" @@ -14,6 +18,10 @@ "Reager med %1$s" "Reager med andre emojier" "Lest av %1$s og %2$s" + + "Lest av %1$s og %2$d andre" + "Lest av %1$s og %2$d andre" + "Lest av %1$s" "Trykk for å vise alle" "Fjern reaksjonen med %1$s" @@ -21,6 +29,7 @@ "Vis passord" "Start en samtale" "Brukermeny" + "Vis detaljer" "Ta opp talemelding." "Stopp opptaket" "Godta" @@ -112,8 +121,13 @@ "Start verifisering" "Trykk for å laste inn kart" "Ta bilde" + "Trykk for alternativer" + "Prøv igjen" + "Løsne" + "Vis i tidslinjen" "Vis kilde" "Ja" + "Serveren din støtter nå en ny, raskere protokoll. Logg ut og logg inn igjen for å oppgradere nå. Ved å gjøre dette nå, unngår du å bli tvunget til å logge ut når den gamle protokollen fjernes senere." "Oppgradering tilgjengelig" "Om" "Retningslinjer for akseptabel bruk" @@ -144,9 +158,13 @@ "Kryptering aktivert" "Skriv inn PIN-koden din" "Feil" + "Det har oppstått en feil, og vil kanskje ikke motta varsler om nye meldinger. Vennligst feilsøk varslinger fra innstillingene. + +Årsak: %1$s." "Alle" "Mislyktes" "Favoritt" + "Favoritt" "Fil" "Fil lagret i Nedlastinger" "Videresend melding" @@ -164,6 +182,7 @@ "%1$d medlemmer" "Melding" + "Meldingshandlinger" "Meldingsoppsett" "Melding fjernet" "Moderne" @@ -229,6 +248,7 @@ "Emne" "Hva er dette rommet for?" "Kan ikke dekryptere" + "Sendt fra en usikker enhet" "Du har ikke tilgang til denne meldingen" "Avsenderens verifiserte identitet er endret" "Invitasjoner kunne ikke sendes til en eller flere brukere." @@ -239,6 +259,7 @@ "Brukernavn" "Verifisering kansellert" "Verifisering fullført" + "Verifisering mislyktes" "Verifisert" "Verifiser enhet" "Verifiser identiteten" @@ -247,6 +268,7 @@ "Venter…" "Venter på denne meldingen" "Du" + "%1$s identiteten ser ut til å ha endret seg. %2$s" "(%1$s)" "Bekreftelse" "Feil" @@ -254,31 +276,72 @@ "Advarsel" "Endringene dine er ikke lagret. Er du sikker på at du vil gå tilbake?" "Lagre endringer?" + "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." "Kunne ikke laste inn meldinger" "%1$s fikk ikke tilgang til lokasjonen din. Vennligst prøv igjen senere." + "Kunne ikke laste opp talemeldingen din." + "Melding ikke funnet" "%1$s har ikke tilgang til lokasjonen din. Du kan aktivere tilgang i Innstillinger." "%1$s har ikke tilgang til lokasjonen din. Aktiver tilgang nedenfor." + "%1$s har ikke tilgang til mikrofonen din. Aktiver tilgang til å ta opp en talemelding." + "Dette kan skyldes nettverks- eller serverproblemer." + "Denne romadressen finnes allerede. Prøv å redigere romadressefeltet eller endre romnavnet" + "Noen tegn er ikke tillatt. Bare bokstaver, sifre og følgende symboler støttes! $ & \'() * +/; =? @ [] - . _" "Noen meldinger er ikke sendt" "Beklager, det oppstod en feil" + "Ektheten til denne krypterte meldingen kan ikke garanteres på denne enheten." + "Kryptert av en tidligere verifisert bruker." "Ikke kryptert." + "Kryptert av en ukjent eller slettet enhet." + "Kryptert med en enhet som ikke er verifisert av eieren." + "Kryptert av en ubekreftet bruker." "🔐️ Bli med meg på %1$s" "Hei, snakk med meg på %1$s: %2$s" "%1$s Android" "Kunne ikke velge medium, prøv igjen." "Kunne ikke behandle medier for opplasting, vennligst prøv igjen." "Opplasting av medier mislyktes, vennligst prøv igjen." + "Fest viktige meldinger slik at de lett kan ses" + + "%1$d Festet melding" + "%1$d Festede meldinger" + + "Festede meldinger" + "Du er i ferd med å gå til %1$s kontoen din for å tilbakestille identiteten din. Etterpå blir du tatt tilbake til appen." + "Kan du ikke bekrefte? Gå til kontoen din for å tilbakestille identiteten din." + "Trekk tilbake verifikasjon og send" + "Du kan trekke tilbake verifiseringen og sende denne meldingen likevel, eller du kan avbryte for nå og prøve igjen senere etter at du har reverifisert %1$s." + "Meldingen din ble ikke sendt fordi %1$ss verifiserte identitet er endret" + "Send melding uansett" + "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" "Kunne ikke behandle medier for opplasting, vennligst prøv igjen." "Kunne ikke hente brukerdetaljer" + "Melding i %1$s" + "%1$s av %2$s" + "%1$s Festede meldinger" + "Laster inn melding…" + "Vis alle" + "Chat" + "Forespørsel om å bli med sendt" "Del lokasjon" "Del min lokasjon" "Åpne i Apple Maps" "Åpne i Google Maps" "Åpne i OpenStreetMap" "Del denne lokasjonen" + "Meldingen ble ikke sendt fordi %1$ss verifiserte identitet er endret." "Meldingen ble ikke sendt fordi %1$s ikke har verifisert alle enheter." + "Meldingen ble ikke sendt fordi du ikke har verifisert en eller flere av enhetene dine." "Lokasjon" "Versjon: %1$s (%2$s)" "en" + "Historiske meldinger er ikke tilgjengelige på denne enheten" + "Du må verifisere denne enheten for å få tilgang til historiske meldinger" + "Du har ikke tilgang til denne meldingen" + "Kan ikke dekryptere meldingen" + "Denne meldingen ble blokkert enten fordi du ikke har bekreftet enheten din, eller fordi avsenderen må bekrefte identiteten din." 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 6a967ab1f7..c2f4cf223b 100644 --- a/libraries/ui-strings/src/main/res/values-nl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nl/translations.xml @@ -29,9 +29,11 @@ "Wachtwoord weergeven" "Begin een oproep" "Gebruikersmenu" + "Details bekijken" "Spraakbericht opnemen." "Opnemen stoppen" "Accepteren" + "Beschrijving toevoegen" "Toevoegen aan tijdlijn" "Terug" "Bellen" @@ -73,7 +75,7 @@ "Meer informatie" "Verlaten" "Gesprek verlaten" - "Ruimte verlaten" + "Kamer verlaten" "Meer laden" "Account beheren" "Apparaten beheren" 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 a09c4635bd..d4087c537e 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -286,6 +286,7 @@ Powód: %1$s." "Tożsamość %1$s mogła ulec zmianie. %2$s" "Wygląda na to, że tożsamość %1$s %2$s uległa zmianie. %3$s" "(%1$s)" + "Zweryfikowana tożsamość %1$s uległa zmianie" "Zweryfikowana tożsamość %1$s %2$s uległa zmianie. %3$s" "Wycofaj weryfikację" "Potwierdzenie" @@ -343,6 +344,7 @@ Powód: %1$s." "Twoja wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń." "Przetwarzanie multimediów do przesłania nie powiodło się, spróbuj ponownie." "Nie można pobrać danych użytkownika" + "Wiadomość w %1$s" "%1$s z %2$s" "%1$s przypiętych wiadomości" "Wczytywanie wiadomości…" 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 184a51c78a..659b8ffd58 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -145,7 +145,9 @@ "Copiado para a área de transferência" "Direitos de autor" "A criar sala…" + "Pedido cancelado" "Saíste da sala" + "Convite recusado" "Escuro" "Erro de decifragem" "Opções de programador" @@ -339,6 +341,7 @@ Razão: %1$s." "A sua mensagem não foi enviada porque não verificou um ou mais dos seus dispositivos" "Falha ao processar multimédia para carregamento, por favor tente novamente." "Não foi possível obter os detalhes de utilizador." + "Mensagem em %1$s" "%1$s de %2$s" "%1$s mensagens afixadas" "A carregar mensagem…" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index cae3b9c90b..b95448dda0 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -147,7 +147,9 @@ "Skopírované do schránky" "Autorské práva" "Vytváranie miestnosti…" + "Žiadosť bola zrušená" "Opustil/a miestnosť" + "Pozvánka bola odmietnutá" "Tmavý" "Chyba dešifrovania" "Možnosti pre vývojárov" @@ -289,6 +291,10 @@ Dôvod: %1$s." "Overená totožnosť používateľa %1$s sa zmenila." "Overená identita používateľa %1$s %2$s sa zmenila. %3$s" "Zrušiť overenie" + "Odkaz %1$s vás presmeruje na inú stránku %2$s + +Naozaj chcete pokračovať?" + "Dôkladne skontrolujte tento odkaz" "Potvrdenie" "Chyba" "Úspech" 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 c7f859df1c..f1497607f8 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -29,6 +29,7 @@ "Visa lösenord" "Starta ett samtal" "Användarmeny" + "Visa detaljer" "Spela in röstmeddelande." "Stoppa inspelning" "Godkänn" @@ -144,13 +145,16 @@ "Kopierad till klippbordet" "Upphovsrätt" "Skapar rum …" + "Begäran avbruten" "Lämnade rummet" + "Inbjudan avvisad" "Mörkt" "Avkrypteringsfel" "Utvecklaralternativ" "Enhets-ID" "Direktchatt" "Visa inte detta igen" + "Nedladdningen misslyckades" "Laddar ner" "(redigerad)" "Redigerar" @@ -168,6 +172,8 @@ Anledning:%1$s." "Favorit" "Favoriter" "Fil" + "Fil borttagen" + "Fil sparad" "Fil sparad i Download" "Vidarebefordra meddelande" "Används ofta" @@ -180,6 +186,7 @@ Anledning:%1$s." "Ljust" "Länk kopierad till klippbordet" "Laddar …" + "Laddar mer …" "%1$d medlem" "%1$d medlemmar" @@ -190,6 +197,7 @@ Anledning:%1$s." "Meddelande borttaget" "Modernt" "Tysta" + "%1$s (%2$s)" "Inga resultat" "Inget rumsnamn" "Frånkopplad" @@ -267,6 +275,7 @@ Anledning:%1$s." "Verifierad" "Verifiera enheten" "Verifiera identitet" + "Verifiera användare" "Video" "Röstmeddelande" "Väntar …" @@ -275,6 +284,7 @@ Anledning:%1$s." "Identitet för %1$s verkar ha ändrats. %2$s" "Identitet för %1$s %2$s verkar ha förändrats. %3$s" "(%1$s)" + "Verifierad identitet för %1$s har ändrats." "Verifierad identitet för %1$s %2$s har ändrats.%3$s" "Återkalla verifiering" "Bekräftelse" @@ -293,6 +303,7 @@ Anledning:%1$s." "%1$s är inte behörig att komma åt din plats. Du kan aktivera åtkomst i Inställningar." "%1$s är inte behörig att komma åt din plats. Aktivera åtkomst nedan." "%1$s är inte behörig att komma åt din mikrofon. Aktivera åtkomst för att spela in ett röstmeddelande." + "Detta kan bero på nätverks- eller serverproblem." "Den här rumsadressen finns redan. Försök att redigera adressfältet för rummet eller ändra rummets namn" "Vissa tecken är inte tillåtna. Endast bokstäver, siffror och följande symboler stöds ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" "Vissa meddelanden har inte skickats" @@ -330,6 +341,7 @@ Anledning:%1$s." "Ditt meddelande skickades inte eftersom du inte har verifierat en eller flera av dina enheter" "Misslyckades att bearbeta media för uppladdning, vänligen pröva igen." "Kunde inte hämta användarinformation" + "Meddelande i %1$s" "%1$s av %2$s" "%1$s Fästa meddelanden" "Laddar meddelande …" 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 bbeefd6eca..ceaa8705de 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -147,7 +147,9 @@ "Скопійовано до буферу обміну" "Авторське право" "Створення кімнати…" + "Запит скасовано" "Виходить з кімнати" + "Запрошення відхилено" "Темна" "Помилка розшифрування" "Налаштування розробника" @@ -286,6 +288,7 @@ "Ідентичність %1$s, схоже, змінилася. %2$s" "Ідентичність %1$s %2$s схоже, змінилася. %3$s" "(%1$s)" + "Верифікована ідентичність %1$s змінилася." "Верифіковано особистість %1$s %2$s змінилася. %3$s" "Відкликати верифікацію" "Підтвердження" @@ -343,6 +346,7 @@ "Ваше повідомлення не було надіслано, оскільки ви не підтвердили один або декілька своїх пристроїв" "Не вдалося обробити медіафайл для завантаження, спробуйте ще раз." "Не вдалося отримати дані користувача" + "Повідомлення в %1$s" "%1$s із %2$s" "%1$s закріплених повідомлень" "Завантаження повідомлення…" 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 80d6f5e134..940a285e91 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 @@ -27,6 +27,7 @@ "顯示密碼" "開始通話" "使用者選單" + "檢視詳細資訊" "錄製語音訊息。" "停止錄音" "接受" @@ -123,7 +124,7 @@ "再試一次" "取消釘選" "在時間軸中檢視" - "檢視原始碼" + "檢視來源" "是" "是的,再試一次" "您的伺服器現在支援新的、更快的協定。立即登出並重新登入以進行升級。現在這樣做將協助您避免在稍後移除舊協定時被強制登出。" @@ -181,6 +182,7 @@ "淺色" "連結已複製到剪貼簿" "載入中…" + "載入更多……" "%1$d 位成員" @@ -190,6 +192,7 @@ "訊息已移除" "現代" "關閉通知" + "%1$s (%2$s)" "查無結果" "無聊天室名稱" "離線" @@ -266,6 +269,7 @@ "已驗證" "驗證裝置" "驗證身份" + "驗證使用者" "影片" "語音訊息" "等待中…" @@ -274,6 +278,7 @@ "%1$s 的身份似乎已變更。%2$s" "%1$s 的 %2$s 身份似乎已變更。%3$s" "(%1$s)" + "%1$s 的已驗證身份變更了。" "%1$s 的 %2$s 驗證身份已變更。%3$s" "撤回驗證" "確認" @@ -292,6 +297,7 @@ "%1$s 沒有權限存取您的位置。您可以到設定中開啟權限。" "%1$s 沒有權限存取您的位置。請在下方開啟權限。" "%1$s 沒有權限存取您的麥克風。您需要開啟權限才能錄製語音訊息。" + "這可能是因為網路或伺服器的問題所致。" "此聊天室地址已存在。請嘗試編輯聊天室地址欄位或變更聊天室名稱" "不允許使用部份字元。僅支援字母、數字與以下符號 ! $ & ‘ ( ) * + / ; = ? @ [ ] - . _" "有些訊息尚未傳送" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 1b50a9e72c..10f3d08e38 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -160,6 +160,7 @@ "Editing" "Editing caption" "* %1$s %2$s" + "Empty file" "Encryption" "Encryption enabled" "Enter your PIN" @@ -184,6 +185,7 @@ Reason: %1$s." "This Matrix ID can\'t be found, so the invite might not be received." "Leaving room" "Light" + "Line copied to clipboard" "Link copied to clipboard" "Loading…" "Loading more…" @@ -287,6 +289,10 @@ Reason: %1$s." "%1$s’s verified identity has changed." "%1$s’s %2$s verified identity has changed. %3$s" "Withdraw verification" + "The link %1$s is taking you to another site %2$s + +Are you sure you want to continue?" + "Double-check this link" "Confirmation" "Error" "Success" diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index e6b1763486..420b32ce77 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -32,7 +32,7 @@ private const val versionYear = 25 private const val versionMonth = 3 // Note: must be in [0,99] -private const val versionReleaseNumber = 1 +private const val versionReleaseNumber = 2 object Versions { const val VERSION_CODE = (2000 + versionYear) * 10_000 + versionMonth * 100 + versionReleaseNumber diff --git a/plugins/src/main/kotlin/extension/locales.kt b/plugins/src/main/kotlin/extension/locales.kt index ef842dbb1f..ab8507c622 100644 --- a/plugins/src/main/kotlin/extension/locales.kt +++ b/plugins/src/main/kotlin/extension/locales.kt @@ -12,6 +12,7 @@ val locales = setOf( "en_US", "es", "et", + "eu", "fa", "fi", "fr", diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png index ceff22f4bc..17246e6487 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1d95d1afd512e555519a7c78b419e13b76cc09ee78a83a46d8257bf3874095e -size 16634 +oid sha256:b48ef870871bbdc901721b1e25caec609b4a2b4e96dd442344d87307cd15b166 +size 18236 diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_de.png index 861a8d4393..77e03e96c5 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8d5f40c8584b2c049997e9507b1e60a1564a0675f7cebb9de7ccb118f988dc4 -size 17300 +oid sha256:d39af29b567ef13015fe236e982a571ab3b19ebeac0ae94d09b3987423ab41e6 +size 18259 diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_de.png index 4da2c57f1b..a380d66caf 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12ffcb678718ecde962445ea022c3a4c21140b41d81882886cca6bcbf6a63f12 -size 16887 +oid sha256:0202c0c34ea077bf521161582ce081493dba6cc806375a1b1cd7173470ea3b07 +size 18463 diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_de.png index 9806f1480f..df56872657 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8807cf26186ee4003f782065d327808759c955814225caea95d54e7fdd8d8347 -size 20818 +oid sha256:1530c35953f8ca11c70f2c372d8309d5a76a23b5967c9d5542b4703910c6369b +size 21701 diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_de.png index 967c006b01..669efaedcb 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80d34a5a17f6a9a5ec602eccac80d68499a1f825d8e9ffb04f23b2e2802545f2 -size 20058 +oid sha256:e7901747c698bd155d36eb7de49e05b2cfed2d522d2548991cf66da34a3d7c9d +size 21504 diff --git a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_de.png b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_de.png index 76de59fad6..ea6d55e56f 100644 --- a/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_de.png +++ b/screenshots/de/features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1738ac762302f022192d97a3d151d2179f52e96408495e602eb6d445b920529b -size 20051 +oid sha256:ab50dfb6f6c49c113ec3ad0d169f917989f8663e83910c375554670379ff60da +size 21709 diff --git a/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_0_de.png b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_0_de.png index 3fe9d6baf0..3016f9b637 100644 --- a/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_0_de.png +++ b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:453003e29c29bb190ea322b422771c0415397c10d202b2c3f153117011a1d029 -size 25929 +oid sha256:1b0db5a0326b9331c3873948d80d6f5ccf956f9c0d46a13bb3a10fa3f4693dcd +size 26675 diff --git a/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_3_de.png b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_3_de.png index e88a7d5767..0773aaf4ad 100644 --- a/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_3_de.png +++ b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:807f31af49d219606cf24c1b982acdb946170fa1f6ee8b92906a70da5ba95d5e -size 52920 +oid sha256:c0ecabe5479d3e0ecb9a41e5ef8f1eedf4acbe5ba9857a772f4bfafb16531c38 +size 53776 diff --git a/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_5_de.png b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_5_de.png new file mode 100644 index 0000000000..5babaa42f7 --- /dev/null +++ b/screenshots/de/features.createroom.impl.root_CreateRoomRootView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5a9b80e68360022416d46a32609b0faaf935872dade6c8a8de6ab327f94d84f +size 30677 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png new file mode 100644 index 0000000000..1e4fe2bd31 --- /dev/null +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:508b822db1bd460155aa83aa7b728b6be52f741204436e8424c4c188eba43976 +size 39027 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png new file mode 100644 index 0000000000..86f1d0a570 --- /dev/null +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d309986d3f518bbcb80c2361261a32d81249f392750955bcd3baeb98a50bef8 +size 32335 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png new file mode 100644 index 0000000000..5855954f9c --- /dev/null +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:239b9d664c83e01bb630322c6ec1b8403412160234659f1af7e32fc2d5d21223 +size 45093 diff --git a/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png new file mode 100644 index 0000000000..98b4077074 --- /dev/null +++ b/screenshots/de/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e42efc9372642593772918785b6f0ca7bf6877ecfe9987eef15f8727c958df17 +size 38732 diff --git a/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_3_de.png b/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_3_de.png index 065525c283..a3a3b6732a 100644 --- a/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_3_de.png +++ b/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cafc872ec44168b89391fa78e3595d145c8c5733e260e23eceea8772a4099cfb -size 10185 +oid sha256:65fefac07fd5f3049d6307feb14a471f2494b3a72696de55230c73074041d3f7 +size 44737 diff --git a/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_5_de.png b/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_5_de.png new file mode 100644 index 0000000000..065525c283 --- /dev/null +++ b/screenshots/de/features.invite.impl.response_AcceptDeclineInviteView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cafc872ec44168b89391fa78e3595d145c8c5733e260e23eceea8772a4099cfb +size 10185 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 34dde50b7d..e7bf765400 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:5d5e76a8e1c2532b8841f3d7f439674053095d7b8fe2b96292b7a5093191dd2f -size 27503 +oid sha256:5cb20afb3c9e97728469a9f791ac2b937daaeaab00b3304b626f02f3f3689fef +size 31608 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 e0a1f14cc4..094b56a1a8 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:685d3a803b8abbd218346ea989c387d38c11c2ce928011d30ef3bf2c94b924c8 -size 36949 +oid sha256:0e0551255bd0278b01196c2c5386df8f000dd46e0eaa577bec9ce2cf59544d82 +size 40468 diff --git a/screenshots/de/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_de.png b/screenshots/de/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_de.png index 991de30e75..4605e3d690 100644 --- a/screenshots/de/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_de.png +++ b/screenshots/de/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00a02aa3270c73862ba8fdc55744076eaf27409e0a1929813b1e5022df8d93f2 -size 55636 +oid sha256:413d5a7e4717c03eaa6f6a023f021f49c21e74f0eff5718fc7b415063a16a065 +size 55534 diff --git a/screenshots/de/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_de.png b/screenshots/de/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_de.png index 73bacb7e93..9a786c5f19 100644 --- a/screenshots/de/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_de.png +++ b/screenshots/de/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5c92dca182754b6a3622c6eb7198d6829d6a696bbb29c8e5745b818e5a7050d -size 60492 +oid sha256:5c1f0c972c34190eb1879dc1eed7a086949df5bea2cb1e91cd8de00250ead618 +size 60325 diff --git a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_de.png b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_de.png index af141729fb..1571627d84 100644 --- a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_de.png +++ b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58162cff6e21876e547201d27cc2d10add12f4db7afaccd4fa3690a3609ffefc -size 54985 +oid sha256:a56642579318271960d92b28d7037c123b70b1983d59351c6bd026a7c136188e +size 55001 diff --git a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png index 823f278742..cf5d80de9a 100644 --- a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png +++ b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e85f9b066bf1cb507482938024cfb9657982620cb63314276d690903a910934c -size 65851 +oid sha256:8267d92b66f9e4aec78d904cd618d55143ecf931fbf6cc540f1776551280b099 +size 65857 diff --git a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png index 22a6f0375d..e25284622c 100644 --- a/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png +++ b/screenshots/de/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71180dfeb480dd3517dcc6bc4abc3d7c52f560237bfadd23163ff9c042249177 -size 70870 +oid sha256:931ff76e4b1cea3e7a739bd2bdf0acd4ea3d30aa3b8e7a61118060daf879fad6 +size 68815 diff --git a/screenshots/de/features.messages.impl.messagecomposer_MessageComposerView_Day_0_de.png b/screenshots/de/features.messages.impl.messagecomposer_MessageComposerView_Day_0_de.png index db4ec335f3..f95dddb777 100644 --- a/screenshots/de/features.messages.impl.messagecomposer_MessageComposerView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.messagecomposer_MessageComposerView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd32b279b0b83416e2bee71fd58bfff35e957b478641481852e2c2e93f60dda8 -size 18454 +oid sha256:aa5028f3fed84036094624136b5101cbb2a983d063cc64d0547021b510eaae5d +size 18550 diff --git a/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_de.png b/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_de.png index dbe19c419c..634b60cfcc 100644 --- a/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_de.png +++ b/screenshots/de/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b43202763e975eb13bac33fab6914faa0565e96c668d515dcec16e88faad5b61 -size 4173 +oid sha256:c14787c550def03ba6823107856be7df3531e3db0578bfded1196fbe1d4c1360 +size 4223 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_0_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_0_de.png index a8bfc8e097..97d7007038 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_0_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55c080b873db37c8fae2bee0cc5b9d30bd84cfb1090af58cba3d2c676e0a52f6 -size 57835 +oid sha256:641cff9b10ab1ca8a084ad0b0382dd3b450a603ac7072c016d37c8d824114ad6 +size 57840 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 3f11bd0073..d1ae1005cc 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:ba0889fff077fb147b13d08dc8d981ac0a50d5596bec5d34158884827b0dc8db -size 57861 +oid sha256:03214da03b4aa5476f7e05a2d8dadd582abf3d5ee3bbf75a3eb97d427c5ff7e7 +size 57868 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png index 32a85c9887..450889e5fe 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c35cc626eb023fbc8521dc99ba4059a07280cf08f73c8f628e3f37f3fca26f11 -size 61180 +oid sha256:ac69e0b114b9459700c35d9473b2bf7ce6f6cc53f4c3c07cab75a54ee5bdd72a +size 61187 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png new file mode 100644 index 0000000000..212e154890 --- /dev/null +++ b/screenshots/de/features.messages.impl_MessagesView_Day_12_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14355288aa81a4ab54b1631359cc1a2c9fd08163659d07d9c47f6e34abf23550 +size 60868 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png index 209ad06506..36b7a6b0f5 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4dd0a7b9971d7ff35fb186e84dd7ca2226589249309249c9a920cd1e2d217d65 -size 57020 +oid sha256:670d8a563187d4f5a0229873c14dc18ed788519eca4c13072e201a165d4ebe12 +size 57033 diff --git a/screenshots/de/features.messages.impl_MessagesView_Day_4_de.png b/screenshots/de/features.messages.impl_MessagesView_Day_4_de.png index aa678223b4..01091aed83 100644 --- a/screenshots/de/features.messages.impl_MessagesView_Day_4_de.png +++ b/screenshots/de/features.messages.impl_MessagesView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bfb6ffce274079b7f069c227cfd32400ac4b967c1dce78295232c03d7520438 -size 55764 +oid sha256:e616713ebba2e6b6baa3b9c050205c8a7f8165d4ccf85bf4ddb4873d2d9cdab5 +size 55788 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 ad2d7fb6e1..1ef37f5295 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:4783bd13ede24f33175ec05e1419091f66b76b49c13fd703eee861c4813a8037 -size 55639 +oid sha256:314db3286f8098503818cc45eeebc0c89987748f0f575cfaea465b430bb87c72 +size 55652 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 61c4c16061..54b7d86aac 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:b6d99ba841fb688b7ee75609467a8e66d64f1e2ef64276b7503af94559c2506e -size 54121 +oid sha256:a12f1f767b92975cefe0c1382980794c301517d151892d78be0f6dced876a605 +size 54191 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 dad8a45b2c..6f1bf5f715 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:fec5ac2131edea81292fa5e58e51d4fb5c46804ad1d59f4e245b82f8440778da -size 59667 +oid sha256:bbffd005a99dac5e62b094d2994c2aabb883b8e5a231e64656caedad6b0c14a6 +size 59693 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 1b276d6f31..538e936a19 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:0a62483717d425b84165a5835e00d3abf3b88fac5ff2a29bf35f28459b1a1119 -size 60305 +oid sha256:bb1ee601ccdad413b35bd2fffe4fdef8a341b6ee19253f5a038c94f71dabc6cb +size 60311 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png index c997d840e2..bbe187127e 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64740e34a5fe06c2d1898099ebb22d97d64c9beaacd1893e5c9532f611a460fd -size 39455 +oid sha256:400fa3effebadae2a8ec24a177a6d497cf8f363fe6caecac90dfccaf67de31c6 +size 39525 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png index 360548ef76..2ae47a638e 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a359ef5989247e5bad9b296332c13d45a2cea0effdb5ed5fea4274e27809a9 -size 42290 +oid sha256:e0f985f494027e1d6c34e09762cdec83d8878dcaf9b57d44717cd6a38447f8bd +size 42247 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png index f425f7499a..84ac297689 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77d3fe86bf862a342feed367207c836ac7377146f9ac8f959af182421420921f -size 42347 +oid sha256:ef2cc7834057ec4d90c040b124acfedbc4c874f30a7784f055d2d675ac63b299 +size 42318 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png index 48a3f09aba..7ffb3dcd7a 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dcf77ad377c62a31eb6d14eea8b88e0bcf81554456b7e74174a1ecf8d81eb3e -size 118553 +oid sha256:ec4720ea8139527df60cb28b482c773e1af96555df87bcc8a96ea34dd42502bc +size 118487 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png index 307b1c2f1d..3383e2faff 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:966319ce3bf2482fe7d6096fb8083fb8dfe0e8eff5f4f83f2b3bd2f171439f1f -size 43202 +oid sha256:8f3dd51cb2a0fca30e4864f777262cc8b6a2f3e0f7295c3fe9ece67275d8ad4f +size 43270 diff --git a/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png b/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png index c334ff0ec6..846e2c168b 100644 --- a/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png +++ b/screenshots/de/features.poll.impl.create_CreatePollView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ee16c82d41d300a5e4a7067c61beb9789e77ea839a88e69c288345d32cb8071 -size 42518 +oid sha256:9721889f9690a7567e4e7839935472092c1540cbc860a7629717bfaf9000d922 +size 42565 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png index 7aea7b47a7..68d2ad3eb4 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8ce478fe32b1fb8479efb86f5305611e5f1237a98f12392acb1a79bfe43af55 -size 68549 +oid sha256:159485ee9a4665985a9426aef3378822f7c4b7e29256e4ee37859c09bafe4383 +size 68434 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png index 55c94e44cf..843819f7a6 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fdd3f6aff2518164dd958d3f4b3ff014e2c4c5f6714525ee9fc15eec6df76cb -size 68305 +oid sha256:4cb88d8596ca78d44537aea79fdcfec1fab17a83943fb0a3799604920b3cfa52 +size 68199 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png index a2a2640dea..1b289a88da 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff632eb1ebd692ce89833cfa0912cdbb2d9eebd1b03fee1dc5c57717bb373c1a -size 34395 +oid sha256:5e04817fd6f0cc5ce732e90499ce7bdc603ebd8b2f91c11430abb006a173eaa7 +size 34348 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png index 50010c6205..1474735234 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80413c20e81da286a97ff62630426bb0700fd48e7fdbf2d039a89c77485c8b36 -size 68356 +oid sha256:f305eef0b9ad16f217851a1d45f829556e56c669c06a931b0d1f61a278759186 +size 68245 diff --git a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png index b908d63738..47e66d6c42 100644 --- a/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png +++ b/screenshots/de/features.preferences.impl.advanced_AdvancedSettingsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9182d5dbaad926abfaf55d80a4af3aa7da6e45054ed2a02fdaefdea081b66506 -size 68344 +oid sha256:5e2ef11551da2cc0581777ec5f89030c80745b5a36cea448e8033d3f256f4332 +size 68246 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 e0259471d5..992975c4f9 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:efdabe2243c6f0362e4c0f73497dcf43b6dc0cc91468674f02be0b2a52285501 -size 39334 +oid sha256:fa46c4f1c496e42527aaf3b14a0d5f93a90ba6e73b7c4404f094794052afc199 +size 39336 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 31b2b6ae03..c4656ef49b 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:1f6f3c64bf44a4883091676539cdb786a64fdf6bf17a9de7276e6fc97a9fa975 -size 39086 +oid sha256:fc2be4299847fc6c662e831dd3f4c2c5825e99019167e603d7099c8d12019b63 +size 39074 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 ce701b511c..ae2e0111fc 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:23c9712facae2d2c3e35b9b01dca14de61991e1dc380765fab6ec009302c9a0c -size 40325 +oid sha256:1f6887d5b72856bce56e4cd098470c7eee7752c77b4fe172d5d85ebfb610acea +size 40393 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 56ec82f38c..2ef5848dd0 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:bebef89369e7317f14b66e8140b532461834e27602b06584f1465d2187569413 -size 40334 +oid sha256:05e62913e000682d099040a024d12e66e25d477c88cfbbbf31c7da9d2536b1ef +size 40400 diff --git a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_de.png b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_de.png index 71ca874985..e258401726 100644 --- a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_de.png +++ b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13b9a41b5649f3b94a11570d0056f0c0efe1d6b6ec606b5ccd9373de34fb2999 -size 19916 +oid sha256:6e62fb12ad96d7aacea7c8287e8b1a4efa4fd51130402fad1d59e5b398697e66 +size 19945 diff --git a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_de.png b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_de.png index bbf37170e8..9161c1ca4b 100644 --- a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_de.png +++ b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0596e0573ed756bebdb6961a13f15ef6e4a6f80053fcaaf28184162aec04665 -size 24333 +oid sha256:d28efe175226cafd37373a41f67480f6f9504d96f06ab71e5a49657a173f684b +size 24395 diff --git a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_de.png b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_de.png index 08f3e09d34..dba76e365b 100644 --- a/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_de.png +++ b/screenshots/de/features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72cbb8c75a67a977ae2fff03738765e0392150cd8c055d349f5b99d60b108065 -size 30104 +oid sha256:0ab3a46a935725d83202a1d1210fbfce8c14d967acc42cf43c91e74a48d93493 +size 30143 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_1_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_1_de.png index c7ee196df8..980d0eed55 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_1_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c263b0b8fd4c6bdf53118aa5ed06ef919ae5973ed370580cd3d6290c455f9c39 -size 12524 +oid sha256:558d21b247b939f98f07388c69b93dd61ca983a0b4c50e1e815b1c50700b6ecd +size 48200 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png index 4bdd05fb18..c7ee196df8 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fc655569ee74f678cf7355bc2cd69349d119c5bee233604f4c11815766371e8 -size 13876 +oid sha256:c263b0b8fd4c6bdf53118aa5ed06ef919ae5973ed370580cd3d6290c455f9c39 +size 12524 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_3_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_3_de.png index c7ee196df8..4bdd05fb18 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_3_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c263b0b8fd4c6bdf53118aa5ed06ef919ae5973ed370580cd3d6290c455f9c39 -size 12524 +oid sha256:1fc655569ee74f678cf7355bc2cd69349d119c5bee233604f4c11815766371e8 +size 13876 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png index d96220d923..c7ee196df8 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:646f754dc9a02989008e469b2fbc8928bd12492c3de4b108c8a64508aae16ad2 -size 7970 +oid sha256:c263b0b8fd4c6bdf53118aa5ed06ef919ae5973ed370580cd3d6290c455f9c39 +size 12524 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_5_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_5_de.png new file mode 100644 index 0000000000..d96220d923 --- /dev/null +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_5_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:646f754dc9a02989008e469b2fbc8928bd12492c3de4b108c8a64508aae16ad2 +size 7970 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_6_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_6_de.png deleted file mode 100644 index 52c1896d28..0000000000 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_6_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:59a0d786c26f707d3db071f2845fd95b6762e33b37ebe9b9ab1b696d6c3b5452 -size 25504 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_7_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_7_de.png index 5e8d68e7ac..52c1896d28 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_7_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8342dae1c9b3769d867d856ecdc3b3302fa5cd94e80c6ad9ceddf482968305f1 -size 12343 +oid sha256:59a0d786c26f707d3db071f2845fd95b6762e33b37ebe9b9ab1b696d6c3b5452 +size 25504 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_8_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_8_de.png index 9886eb1f07..5e8d68e7ac 100644 --- a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_8_de.png +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f29deb95619350659f5c1932c72d06edb952c06bb894d2afec87282503ce266 -size 21647 +oid sha256:8342dae1c9b3769d867d856ecdc3b3302fa5cd94e80c6ad9ceddf482968305f1 +size 12343 diff --git a/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_9_de.png b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_9_de.png new file mode 100644 index 0000000000..9886eb1f07 --- /dev/null +++ b/screenshots/de/features.roomdetails.impl.members_RoomMemberListView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f29deb95619350659f5c1932c72d06edb952c06bb894d2afec87282503ce266 +size 21647 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_de.png index 31e866efcb..caafaf7ca6 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3f460e8cda9fefa69a959ccf8fe4e55b75aee26ffd023693c3dd4ace35a4d7c -size 49643 +oid sha256:d28585e9ea9181479d9663f363f37fd0c13a9f9f588f2b0b6db7d719aef458a4 +size 49684 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_de.png index 5a457c8568..4194794abb 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:320bd1d50cd859c451dcaa572941e65908d9c3969060d6a21b3266ed69848d66 -size 49353 +oid sha256:5a461db38dd589c5ee1ee941039d2eb18c057d2434ab0bfe783f1b1709d45aa5 +size 49352 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_de.png index 074061670e..45a4139b58 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dd404232368b1c4453ed59580a85a3ecda2dca8d099ea781d187c6915097824 -size 63376 +oid sha256:2813315c750506129d4b266eaf8d21744f67ae2e2abbf288a80744d0b054ff82 +size 63386 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_de.png index 0732360107..7253c239ff 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dce82d26bc3e5b6fd1296777229e05f02c289917e22e5f2b92b944641b0cfdf2 -size 44714 +oid sha256:f83b333a0e6b8307899acda628be6d67ecfd747c69bc2fb4f4214e0a22d7edd8 +size 44797 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_de.png index ce3c28cb13..ff83b562f5 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a5e363beec8ec4c1b3de4cf0cb4cc0e9a213e4f5dfddde9ffcdc67d34f8117e -size 42916 +oid sha256:4c4cb56fe86cdd2eeacb75867669f3dc3fe3cb8671265fde7bde6f7deed1451a +size 42998 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_de.png index 0d6d43472e..689500ca1a 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:538cf66b631b2f6bbbf8cbb0b62d574d3879f014edb1d23e2737e23c51bdda0a -size 56047 +oid sha256:ba623b0e9e168e86fb9be1af81323de754ee05f659ba627bd6e40178bf27f433 +size 56134 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_de.png index 0732360107..7253c239ff 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dce82d26bc3e5b6fd1296777229e05f02c289917e22e5f2b92b944641b0cfdf2 -size 44714 +oid sha256:f83b333a0e6b8307899acda628be6d67ecfd747c69bc2fb4f4214e0a22d7edd8 +size 44797 diff --git a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_de.png b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_de.png index 2a3b7a1cab..6acbbaba8e 100644 --- a/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_de.png +++ b/screenshots/de/features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32ed3ea41ca7ed350b0d298bd9e7a510527ed4e38331290f12d8c7f08829b590 -size 42371 +oid sha256:0e36fa2831ff08a7406e60c7a99754e1f7d7d9e53469585ff37180fc056bb6d5 +size 42498 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png index b18b924d6b..1d3cfbf6d0 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07a8c391d2cc558c4d898f3753dd9c609989ac4c445c7dc36d9ea287e84bb6ed -size 45908 +oid sha256:63fed56c9fc1d4553d39d7139b4bf22a5f68c0937bf38fef91b100ef36c6d47f +size 45891 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png index b562eb7f35..4c1688a458 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48b62c195501784ad5cf8c74b3ee4379ee89f7fa379924fff8da0d6a54a3fc80 -size 44771 +oid sha256:ddc40eb87892667798159d8527dc92e63f4f50fc09da65f5b6cd94ed771f511e +size 44774 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png index 98e1853042..d7fb40d3bc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d2780d409e4b68a57436c2b9d6dd376d0b6bb99aa59e2dad89ba46d8a967e9c -size 43235 +oid sha256:1d38a07867e68e0bc2297ec94deacf33701a057ec3ac4826c9315ba343a9cc55 +size 43233 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png index b822cf1936..71823e69ae 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40b725095df8ea2a70c720fa5e12a45641cfbd8e50f5fac9c76d958f01aab15d -size 47028 +oid sha256:4f22d0630fba2b489cd76c2599617898b9b02599a14568c7d85e1dcf396c2233 +size 47034 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png index 576aefd348..69d5e38955 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aed024a57f5d0262bf27682a1b914b98445f8a9729536bcc3157f32c152801b5 -size 45149 +oid sha256:57dd92d89f9d9501dae6a2d86ea09aca8c90778d089ef12b1f5f8167e2e99fed +size 45123 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png index ac6ea3e71c..8774259edc 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b00f0018d0fc3ee04d9ad7ad0cad6307e78f17ab039eb1b7396e99adc13b91a8 -size 45651 +oid sha256:a26db933f780f800524a73ceb44bde810144fade08b422f7f01d086ba52a78f4 +size 45632 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png index ee58cfa693..064c2df5e7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8f5e3e26565fe0c2f448e1b591a48c846032c5075adfa72d70ef7f4e3a8a3d4 -size 46228 +oid sha256:96de74399c8eaa9ea09d1864a2b5984dd082004696300ad1ff59aa0a47bb85fa +size 46213 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png new file mode 100644 index 0000000000..d2627d7694 --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_16_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24e9b81b9bcd5f879d0bc47244a7b9938912d223e15f050dc503e871a4a192fe +size 45451 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png new file mode 100644 index 0000000000..4fbcecfb3c --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_17_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3182cd1fb86194493ef923e46f5ade74ffd48b6a28a5f42c88dfbaa022736682 +size 41859 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png new file mode 100644 index 0000000000..55772678a6 --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_18_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:487b634d922beecb2d235a73d325f052ea904b8c5a053c4856ad9158cd9b47aa +size 41814 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png index 234cf4a245..5225709c8e 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:349ab184ea5e4f997716b416f7f6fd5a91dc454a8aa2b10a9ba99def50b23bb4 -size 36353 +oid sha256:68c3b3ce803a898c5121dc1c1eb19741dbb42d6dcfc4bed65aedf7ef91800124 +size 36350 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png index e372a88469..b86aaac933 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff31b2f4719d9a554c7549c112028e2af7724d95d2d2be5ceb0cdd584501f6be +oid sha256:b5c3457ac233e54c18ebdf8baf0d331003be80fd342a6f91bd0257bb89f79a84 size 38958 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png index de3611059e..13dde4af94 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa2b59794eef63611665f070c6d85a271f78b660ba04524b0c223d3866c0670d -size 45365 +oid sha256:5edc6988867158bf6617bbf5988b0cc942b5d70eba1b4c70ef96b54712064117 +size 45369 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png index 0c4c9196a8..2b8fbd332a 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d07c4de0a435906e8c454679f1a80f5302367e5d324d40fc8757f7f74b9f34c4 -size 44342 +oid sha256:c555ed0912499e49c304e04e907beeedd3772b70795e875416a8208a441403bf +size 44348 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png index 66ad0e569b..374d8eb94f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a51280210418d53b8d73050683046f374821f800fda47a46e250aaf6521eb99 -size 42628 +oid sha256:31e3a894473f061f2ee8dec48e9730ca84a6debcb8023b54b6510e20d8f64058 +size 41499 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png index 208550206e..4b6ec61cb4 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf25fb9ceda184f22065762e3227fd6724e2dfb77121b30f47a47b433ddaf52c -size 46471 +oid sha256:3c00dfa334b1cae6046910f4e372a57f04e1e093e21568b513bb004740dd95b9 +size 45082 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png index 9ad9b5c54a..0e8d76f4cb 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:668642b5da82c6c1e03287ae7557fd06ec5bd8569b7f613a651efdd6ec7f24ed -size 46348 +oid sha256:9340f8533b032a3aa342dfecc4af6593729782c09e1b2909f622d83318bc057f +size 46352 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png index 13fda6c0b4..300da10027 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec2398d7ea5ffbff23d7966dd699ce8450bd7694df1f0d8e73db6b3357a287cb -size 45144 +oid sha256:e342cdbd1c5328b988d722f58cf8e4f456fa2d04647cb7206ae6d1abbcd5277c +size 45146 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png index 9a54914892..775105c877 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetailsDark_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:365a9e0e2b86992b4f7c8aa14a0819792624b3381829573292559c352f24d64d -size 43924 +oid sha256:97e9cfc23cec7a266d2e4f5de78a9f26eb1f4aa5383ac0888e43445a39c6f124 +size 43929 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png index 9d84d1b52b..df75672834 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2220b77585ebecd01427ca8a7b41c877e5c7b62b98ac37d77aab0fa5c5c2a7b4 -size 47096 +oid sha256:234d20bccc37f0d6a28a3dbb181088a4bab5b6a74ded1709285510b6ae7018ce +size 47053 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png index b5c29ea19f..52164d70e7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8531f8c0350f01d24018a13cae604730c8ccc2c4b96a72f3d855b0c8f2fa51c -size 45897 +oid sha256:53286513a6842be215198c9bc0996caa3d75d74d622b07c328c59733096e4f90 +size 45888 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png index 4e259ee09c..4be06edf5f 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5c051a74b4cb1c74927e5dacf0529b484ab043deaf114aa0925e21f38c60b4e -size 44325 +oid sha256:15dad2dfa5ad660d8aef625be453432450b013d4b7f76979b6bbb21a2d5862be +size 44323 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png index a4ef4d2010..1b6d35e2ae 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_12_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3870a99564a838aa67d309ad64906b254fdce051de72ea080ce3ebe19f65ee56 -size 47730 +oid sha256:e3970140bb648ac70889dd165eebdef1cf609d89805af448f32665de6a7ab81a +size 47718 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png index eddaea9b13..3952181b5d 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_13_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ea61627553eb8b450fefa33760441255e76c2bb37a314ffd0f78046fe8740e4 -size 46265 +oid sha256:149b76592b74cb2d2215e6de6604c43b6602ca55e037a041f938a69ba10f8d64 +size 46217 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png index dc3347c71d..8adcf955d1 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_14_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d70c237cc9e617333f4323b9c8a3fbbb6589f9d86bfe9c513d23b574b728e224 -size 46748 +oid sha256:b34833ab2ff3b95885edbc113668409f27c410530ee27fe32f1d2d02ba473e22 +size 46721 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png index 81a0c84a77..594027c0b6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_15_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f321b222fc8b3fdcefb3e210f1b8aae1606cbde5a22c4a3ce88928f6aae7b444 -size 47337 +oid sha256:fc5cbcf1c9d1e479b202f64e1bbc2969ec6b0d2c3e75c005f61b27c63a4b5c93 +size 47313 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png new file mode 100644 index 0000000000..4a640ec48e --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_16_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f3cae332439b8608cb2a6610e8febc6c8fed5349c8e722e58a43a93d4467146 +size 46566 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png new file mode 100644 index 0000000000..1908f0d697 --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_17_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82b74717000cf7d292a4fb9690e308e7f22db1a98e0231c6ef1cea8945e79418 +size 42883 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png new file mode 100644 index 0000000000..7615ea6f5e --- /dev/null +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_18_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:156cf5bf138965d37b3ec8ffc88facb206c77cd82461b642c747b217b6bb6b53 +size 42761 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png index 822b689ce5..4b2d354878 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8971ccc7bf338426bd4be2462c5b701dcf1e707177e8a89971ca54d8c606e8ed -size 37412 +oid sha256:8adb2a2795c647189bb9d6d0ebe8bc71e6f8869f11f13e80f6c075e3019a29cc +size 37394 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png index 3a31f57629..0bb1e00462 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46d520c721b650eb9c6c376f01e373a356301932ab514bfb0acec05dfd20daea -size 40137 +oid sha256:bfa82fd5e8cedd060cc45f5383e66f1826995d227e7df70b6254a288a9cc7035 +size 40130 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png index bbf3349a05..7cc4eafcd2 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:765c5aefb57262bc61d59ff999c21134dc87310d8b1f866ea0cb8ed00cc6e1bb -size 46477 +oid sha256:a2ff6c7c3c3fa28bc6d4772179ea9011d6e8dad6fc3e8ca12de3210fa6398591 +size 46468 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png index e2155154fd..6ac7d80460 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08facf3c11eb066f996a7407087db75ec292e58dd878cca5feb84224d7bb2568 -size 45418 +oid sha256:12c5d18e668bdc14dc48dc4654df23a9da1287cfa12fc77d192c3174de3b123d +size 45406 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png index 131597dd2c..01095a7bf7 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e89603d60ef636eac9c9104f48ac8ff10e8028c0e02cd3f2de5c07303a986964 -size 43658 +oid sha256:5cb95f7267a454057e5fe2020f55b3c066110c08ac2d509443aa449daf95b2fb +size 42459 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png index 1207618b2b..8b56afe5b6 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c82ba0de31628eebad5733da29a11b75ec2cc11f585d5a84fc01dbc9789256e -size 47797 +oid sha256:7dfb941ee934e9b4085e63d47da08b1fdc0d3afbae6d9f39acbe4f120d13f161 +size 46251 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png index bd8a57f5eb..de77906f44 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9c06e0a3a0d552270937bd572ec67d81e8ac4675f2a52e7fe14f6d54d7e6277 -size 47552 +oid sha256:0581402b82ade016a325f9995f2c57266bf5550cacd48fab946c37844c60eb31 +size 47538 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png index 4c18624c38..e954250c55 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7787b6c73b19d10fa466e7a5147bf63d35a0ca7119ac700dcd4056880a810591 -size 46327 +oid sha256:8b049e087d71fdcc8dcce50930354095a493d4246578147616865a35a4a96e65 +size 46316 diff --git a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png index de7ab9791e..3786a782fd 100644 --- a/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png +++ b/screenshots/de/features.roomdetails.impl_RoomDetails_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6c2dd228e6edc82ef865f7801c49b5cdb6d895ff959752682ac91ad496a1a88 -size 44970 +oid sha256:5fc879b5aebe89c33300e31081c06376b144d8b555e6cae9c7702d814e24cab4 +size 44963 diff --git a/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_1_de.png b/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_1_de.png index 13944c3f8f..43ab5961d1 100644 --- a/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_1_de.png +++ b/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2484eb3529923de1506be6d958b3682a283c4cac800edc913532f3eb83b3142d -size 20132 +oid sha256:7f129db2d81936d77fb680a989bd32a43281b3fb44e2e98e636f40796373b1e4 +size 46016 diff --git a/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_2_de.png b/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_2_de.png deleted file mode 100644 index 43ab5961d1..0000000000 --- a/screenshots/de/features.roomlist.impl.search_RoomListSearchContent_Day_2_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f129db2d81936d77fb680a989bd32a43281b3fb44e2e98e636f40796373b1e4 -size 46016 diff --git a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png index c5633cd09b..009c89fd9a 100644 --- a/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png +++ b/screenshots/de/features.securebackup.impl.root_SecureBackupRootView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b431982e400c16c24b8e97ab4bcc70c8c5af25f929a2f7ae2679c32483cb142d -size 47347 +oid sha256:a7a354cd460888edd65a2185602d967314c620a2b3092d7c3eadc6e203d5cb13 +size 47214 diff --git a/screenshots/de/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_de.png b/screenshots/de/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_de.png new file mode 100644 index 0000000000..f2e0575f49 --- /dev/null +++ b/screenshots/de/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3dc3fadf9738dee8425469ddaa0a142ae2933f91bbedbfadc0cf83a16a02297a +size 26786 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png index 91673591de..7aed4754d7 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a7a3dcec6b89773989141656ce91f18e9858704c232c792b84d0d751dbfc4da -size 34336 +oid sha256:b4e22f909849f9b237c9d26db4bd6fed0bf84da7db6086b8557a29e8736e06ff +size 26263 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png index 5e905e8945..fd4228a1d9 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebde2f52d70f1380d728d49dcc031bd0997eca9ba73b6d7823ab6340ade680ab -size 32016 +oid sha256:582e49a18b7b0289467db2bf508714ee22a88ef559aa203d6359cba932244985 +size 24210 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png index af9c16ac5c..4fdc2e7aa2 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9c15ff098a37b077314098ea85d91e58c0a02b1e874efe7ddc0ff59763d686 -size 45192 +oid sha256:3ca641bcf4aaa1e97893e97395edda4af40eb07bb94cc4998a512e158717c753 +size 45161 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png index 1ecb38702e..71aae98e23 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e6d39dae1ee032c4b998ebcf707a0501581f45bd089274c4cdfb1f5703e6993 -size 39494 +oid sha256:7cde506275d63f7b078fad1b30799fda8f73ed6fadc44a8444f38c773ec74eeb +size 37356 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png index 04e6b2c5b0..2494e35600 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:baeb3d81492c8540d46fa87f616fea12eb806057c23e09ed943626fc44dca41d -size 31314 +oid sha256:dba4059e750861096ba06be122b8c0b9e260de5349708223d323b9d45f356b79 +size 25720 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png index 539ff54eba..394a306cd1 100644 --- a/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:879149d98226949ccf42917e1931d1aa30b4679cf7afbb8d5ed8f4b766d99803 -size 35382 +oid sha256:c14ca5d1fc33c203fa2b37d9c332bde076d55ae2695ff66463dc885a87b08ad6 +size 27362 diff --git a/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png b/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png new file mode 100644 index 0000000000..e5b1205cd6 --- /dev/null +++ b/screenshots/de/features.userprofile.shared_UserProfileView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:db2b1cd6f89552691e28f252e45ba54e1049e4f22ce4ffbc98a8f1dd903bb827 +size 36614 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 new file mode 100644 index 0000000000..2eaf5fa55f --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c28614a63922d6f87c37573037b5c6d3beff0ffdb0456a7c6c6cafecc2e68ca0 +size 36295 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png new file mode 100644 index 0000000000..a5f97ddead --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91603c6bd26efcf41d06cb12e21795d61c9c6b24048610365be052416aa18a06 +size 29744 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_de.png new file mode 100644 index 0000000000..5913be82d4 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:748818c809f99f2fdadf30f29565ca959fa5e023817563f70621cdb2628150f5 +size 35187 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_de.png new file mode 100644 index 0000000000..5913be82d4 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:748818c809f99f2fdadf30f29565ca959fa5e023817563f70621cdb2628150f5 +size 35187 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 be16eacf94..5432f843d7 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:2344315569fc09dcaf0779bfeb23699bc9d952734c45528998c71db3793537b0 -size 51122 +oid sha256:7b5b8f059789167f8e1f2eb20cf79be5e90b6dc97ed45bc9cf64ebacaa29f85d +size 50063 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 46e6294575..c265e62b22 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:d7f3163ec709cfe5b91bb5132e9af92195796c9c7a9c0091881579aaee6e4779 -size 53249 +oid sha256:f14d3f79cba2389579654cc81e942deded5b575bbf7ff721e5a5fd9743d21eb2 +size 41463 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png index 647e59e625..d4a9dbd6b2 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46b9be8091c4ad73fd62858745eb1c4cbf3f14e00cf7ef2f5c857ff3f86e931d -size 49985 +oid sha256:4f2ae7b8478ce6be0a40d594b9a79cf16940de4811fed16f988f006157aed55a +size 46639 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 0e4996280a..b23f1584ee 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:fb3f5d8713fadc880ba0fc382b75ab1ba6f7561d2112e6027bf0cd44e3d75db6 -size 35691 +oid sha256:0b049ba6c08eda18c5d2d023b3997a8781715922d9c90473eaef2c98cd0198fe +size 36830 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 1e285a11f8..0d467d87d6 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:ce43d0752859845ecff400c5ef183776c2d9b27c76a0b8c301bc90f00ecea359 -size 23869 +oid sha256:ec92b764faabe7cb103dc55a0e40a3f70ee0200bd05f47dbfd0e9a3e152babe8 +size 53199 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png index cbd3cae660..727ead3f37 100644 --- a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d81823d6d2186ed14991915c26ca879c9f6d9c8f70208806747b1ecf97881df -size 34798 +oid sha256:e9c0ae57b88072c0cfe6f63341ae814b231f4caedd41d3f01a8b95f6dc219ce3 +size 55616 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 cbd3cae660..a5b9d28bc2 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:1d81823d6d2186ed14991915c26ca879c9f6d9c8f70208806747b1ecf97881df -size 34798 +oid sha256:2f756e71169942a4c53b470e393aff886299d8a5dc4a23e70403c33b2971927f +size 44303 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_de.png new file mode 100644 index 0000000000..778e875ff7 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80acb36a54c1d113f36f54b647f2355b529c845c0d87c1f0ea1c717cdfcf647b +size 46801 diff --git a/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_de.png b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_de.png new file mode 100644 index 0000000000..9aec3038eb --- /dev/null +++ b/screenshots/de/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2fc0bc2ccdb1134f06175d302b90d4a79e80e394fb2ed1f64e6136fb5f7ca9ad +size 35645 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png index 5855954f9c..a49c1f674e 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:239b9d664c83e01bb630322c6ec1b8403412160234659f1af7e32fc2d5d21223 -size 45093 +oid sha256:ef8a7a9c83f8fb4a325b76244f58f9aa1484b14038a22c2d896887d0d1f610d4 +size 40512 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png index 5217841216..1c98ae42b3 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d30a2be5e36613fccf87cbdc01a843e5edb10f9a54a4326d2d35e140006ba953 -size 41634 +oid sha256:fa8f20efab9e48ccd07435d5408b34edfc6c2fca6882279c3121d92edb5bddc3 +size 24678 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_de.png new file mode 100644 index 0000000000..7f506bfc38 --- /dev/null +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d94fc082312e356145540c27959d454604dfdee4d7dade7a0ef5a6b94e223a7f +size 29895 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_de.png deleted file mode 100644 index a50a118376..0000000000 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_de.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82cf55db296f552509cb95b2788a234b374c05fca535c64f8cc26bb745237ff1 -size 40103 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png index 8838a8c03f..d707bf8bff 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4517f769957b03ee091627c89b801b60aae60772379401d6e706be2f18f4eef1 -size 35830 +oid sha256:e6b07a3c79685c5d5358f9b21dde72b32331cfcf686d9378cb89076a1cc42695 +size 43862 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png index 46e6294575..45d5da6ce8 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7f3163ec709cfe5b91bb5132e9af92195796c9c7a9c0091881579aaee6e4779 -size 53249 +oid sha256:7bb7b2631bfa9c7829b3320f0374137d9157ff9465a6d6a0843476cb1c1aef33 +size 29952 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png index 647e59e625..b1d180c087 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46b9be8091c4ad73fd62858745eb1c4cbf3f14e00cf7ef2f5c857ff3f86e931d -size 49985 +oid sha256:0d2466557382bab1b8222e22e8f559ae90bc2b156e8b62eba911721f85ad6c60 +size 26066 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png index 858e3c7b1f..b568b9962c 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:601cf725f0726b8e5ae89dbdef40eb01e9adc8711c6194745a3b2425a10857ae -size 34798 +oid sha256:b18ded952183b59563b6293eb3b6edca8c966e0a681767dbee58fd6c734f2dde +size 53609 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png index 4e9a37710d..e4fdd91486 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebfcdb2fa38a6278616267461efeb290318c4fd21e0509ec47f5c4ab848381d8 -size 21512 +oid sha256:bd4b570ea1dba17d82433e5615ca791b7e6ee4cb6580546ac9df381f36f9491a +size 56006 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png index 0e4996280a..a5b9d28bc2 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb3f5d8713fadc880ba0fc382b75ab1ba6f7561d2112e6027bf0cd44e3d75db6 -size 35691 +oid sha256:2f756e71169942a4c53b470e393aff886299d8a5dc4a23e70403c33b2971927f +size 44303 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png index 5855954f9c..ee0007f60a 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:239b9d664c83e01bb630322c6ec1b8403412160234659f1af7e32fc2d5d21223 -size 45093 +oid sha256:29ef55e994b568cb85c0ce53b085fd4b33cc9bc754c9f9ce26f63fe73250213e +size 35188 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png index 1e4fe2bd31..b64b97d813 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:508b822db1bd460155aa83aa7b728b6be52f741204436e8424c4c188eba43976 -size 39027 +oid sha256:9612677d8baf6254ab6858390c839d88b32ee2accb34974429431cf3541e5bd4 +size 21896 diff --git a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png index f7d7cc391e..45cd8b45ae 100644 --- a/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png +++ b/screenshots/de/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a25fac8253b25ba11ea770b29c292f85be184899274724016cbbc03b913692ce -size 21581 +oid sha256:b0df619bbb523c54e388f04bb8d4a92b8a2671d36a9d0b6a441ca402193688dc +size 36057 diff --git a/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png b/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png index ccc085500a..5cb2d38b7b 100644 --- a/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:892e25243ae45a13dbbb6e0065154eb7e4581c8877a2dc57a82f8333d524fe70 -size 55344 +oid sha256:d669231924e75be81e8bc77ab83cbca830d9ac4931bbc232f2002d45ae0a7137 +size 55492 diff --git a/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png index ccc085500a..5cb2d38b7b 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerEdit_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:892e25243ae45a13dbbb6e0065154eb7e4581c8877a2dc57a82f8333d524fe70 -size 55344 +oid sha256:d669231924e75be81e8bc77ab83cbca830d9ac4931bbc232f2002d45ae0a7137 +size 55492 diff --git a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png index 22873fc7c6..fef50a045d 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerFormatting_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b178a5f50404a7104aa3a647158be01e173bdaeca63036d3ef5f9d69c5dcd18f -size 52990 +oid sha256:957d3b10f0165461fb30cccd0891731ad30598f3808803f3fd194f62632f2522 +size 53407 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png index 9081a2f732..d4187835c3 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b6804e4c9415ba75da5ca9b16be07c6b1eda1b6d84e2d563ef692cb93617d32 -size 75820 +oid sha256:8f18ce9ded3bedf206ba07aca96c9e16cf926ce5ddc586784dd5c3f47dec8184 +size 75949 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png index 65b9db12be..f5e26a4e0e 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_10_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3faaf28767463aa8fe5961a61c6fdf992c7d618b19733bc91150471a88020cb6 -size 58864 +oid sha256:c83569820b8ee4e64dc840c8731c306e44f4af0f2e96905db27b19c34804967b +size 58944 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png index 855cf60dfa..37cdd5f0d4 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd95839670f71742c53576bdd22635a389a71bf77c209cd39f375e451abd31c8 -size 73917 +oid sha256:260d9da9bf947755948d1a6b348194f142879409bb9cdb86fe1d00751fa8ac3f +size 74020 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png index a53d365ccd..847c9e3deb 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d6aa5d6f29e38888aaaf9539f66bd3235c9ea8a9096e48a6538086057b0b473 -size 85309 +oid sha256:79d6c9484a7eebbd53b0d6ed7933eb9cc74e976069f71bb8ee39bde52bd047db +size 85407 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png index 4b664b0055..2fa288ec23 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b853ffe20e9d4c8c10c86caec538eb302b496d7c18232ac0325a498085cc9e4 -size 62069 +oid sha256:cac51fdd3736bf9fae45c28ae4df283dda473acd0baf0ee2ae5415ed73d22327 +size 62157 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png index 7bc5ffeff6..85bbb9cc75 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a59655841c958d2f59a6ac0e52d1f076e670298be6d373f7dc40c61276c6e4c -size 61151 +oid sha256:db2f2d07e4d23b316f9309cacfe5e402cf5e230dcb7bc4efef4566e40d798f42 +size 61245 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png index 86b1424d2d..0165e92451 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc3d3b82f386467567eefa83522d21c6d93c0561f5e5cc6301cbacf521686802 -size 68386 +oid sha256:3b27d85232cba50fbeeb5758ecee65594a4660fa8ec670d55b2ccbc0b97d50a9 +size 68483 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png index 72f44ed719..19ad78ebb1 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afe16af06bfedd3b030f3a7d27e8d8f63e89068625c3530c71f50a627fcc6a07 -size 59291 +oid sha256:0cc2e5d586e6296641c37635a8e316f2527c92cf1c21f7ef9915dab01da195e5 +size 59362 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png index d111477810..c166ee4899 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_6_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93eeb7258381e4e5026226a6226da75975fd3db36d28c57359df0fc8f832bfc4 -size 60072 +oid sha256:0daee317f93a38becad1776e9fbdfc98df8a5b27fdef2befd32e51a556b8ab01 +size 60172 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png index 37d95cb0e5..b55a0df772 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_7_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3da9946e0cfc496d0619f57c7bb67b0a737970fc62aeaeed49d9a43ff84beef -size 62290 +oid sha256:8d01926e2e41de43b6e5bfc691a3d21fc7aea31ee62e578130076c8c74554e6b +size 62377 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png index d55b3812f3..afc45ef5c5 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c00782f67da13a617718e50dbb6b21e6e4ded8ee29ac1c6e2eb6ae8374050b78 -size 70410 +oid sha256:bbb6197fb4f6eeaea613ef2d609fac94e027d5fcc5172718bc1ac9fc925b4031 +size 70498 diff --git a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png index 76a546a60c..1008408a72 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerReply_Day_9_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1906f2a814ab6ddb0f6ca509f2eb28b45515285f6db2acb7822c2c72d49b5884 -size 59543 +oid sha256:eb7fb190e297edbdc2cd3b715bda9e65bfe366dc4e4c334eb05a713e59948c88 +size 59622 diff --git a/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png b/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png index 491ce0d9f0..aac16a3f92 100644 --- a/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png +++ b/screenshots/de/libraries.textcomposer_TextComposerSimple_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fe8f3ac71d021e8799b9536bcc81e8570d742d768f3b07fc87bc9f31f17f0e7 -size 46244 +oid sha256:2faa715f5cf4af59c20c6126d6eaccce631f974e746ad595da8c62000b6138dd +size 46301 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 9d82b48892..a01af7db50 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,60 +1,61 @@ // 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",20147,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20161,], ["features.invite.impl.response_AcceptDeclineInviteView_Day_0_en","features.invite.impl.response_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",20147,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20147,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20147,], -["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20147,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20147,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20147,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20147,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20147,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20147,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_1_en","features.invite.impl.response_AcceptDeclineInviteView_Night_1_en",20161,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_2_en","features.invite.impl.response_AcceptDeclineInviteView_Night_2_en",20161,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_3_en","features.invite.impl.response_AcceptDeclineInviteView_Night_3_en",20161,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_4_en","features.invite.impl.response_AcceptDeclineInviteView_Night_4_en",20161,], +["features.invite.impl.response_AcceptDeclineInviteView_Day_5_en","features.invite.impl.response_AcceptDeclineInviteView_Night_5_en",20161,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20161,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20161,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20161,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20161,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20161,], ["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,], ["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",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20147,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20161,], ["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",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20147,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20147,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20147,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20147,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20147,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20147,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20147,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20147,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20147,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20147,], -["features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en",20147,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20147,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20147,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20147,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20147,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20147,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20161,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20161,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20161,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20161,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20161,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20161,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_0_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_0_en",20161,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_1_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_1_en",20161,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_2_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_2_en",20161,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_3_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_3_en",20161,], +["features.preferences.impl.advanced_AdvancedSettingsView_Day_4_en","features.preferences.impl.advanced_AdvancedSettingsView_Night_4_en",20161,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20161,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20161,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20161,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20161,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20161,], ["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",20147,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20161,], ["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",20147,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20161,], ["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",20147,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20161,], ["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",20147,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20161,], ["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",20147,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20161,], ["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,], @@ -64,18 +65,18 @@ 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","",20147,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20147,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20147,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20147,], -["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20147,], -["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20147,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20161,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20161,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20161,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20161,], +["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20161,], +["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20161,], ["features.messages.impl.attachments.preview_AttachmentsView_6_en","",0,], -["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20147,], +["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20161,], ["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",20147,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20161,], ["features.knockrequests.impl.banner_AvatarRowRtl_Day_0_en","features.knockrequests.impl.banner_AvatarRowRtl_Night_0_en",0,], ["features.knockrequests.impl.banner_AvatarRowRtl_Day_1_en","features.knockrequests.impl.banner_AvatarRowRtl_Night_1_en",0,], ["features.knockrequests.impl.banner_AvatarRowRtl_Day_2_en","features.knockrequests.impl.banner_AvatarRowRtl_Night_2_en",0,], @@ -175,18 +176,21 @@ export const screenshots = [ ["libraries.designsystem.components.avatar_Avatar_Avatars_88_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_89_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_8_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_90_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_91_en","",0,], +["libraries.designsystem.components.avatar_Avatar_Avatars_92_en","",0,], ["libraries.designsystem.components.avatar_Avatar_Avatars_9_en","",0,], ["libraries.designsystem.components.button_BackButton_Buttons_en","",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], ["libraries.designsystem.components_BigCheckmark_Day_0_en","libraries.designsystem.components_BigCheckmark_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",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20147,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20147,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20161,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20161,], ["libraries.designsystem.components_BloomInitials_Day_0_en","libraries.designsystem.components_BloomInitials_Night_0_en",0,], ["libraries.designsystem.components_BloomInitials_Day_1_en","libraries.designsystem.components_BloomInitials_Night_1_en",0,], ["libraries.designsystem.components_BloomInitials_Day_2_en","libraries.designsystem.components_BloomInitials_Night_2_en",0,], @@ -197,150 +201,153 @@ export const screenshots = [ ["libraries.designsystem.components_BloomInitials_Day_7_en","libraries.designsystem.components_BloomInitials_Night_7_en",0,], ["libraries.designsystem.components_Bloom_Day_0_en","libraries.designsystem.components_Bloom_Night_0_en",0,], ["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",20147,], -["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20147,], -["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20147,], -["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20147,], -["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20147,], +["features.rageshake.impl.bugreport_BugReportView_Day_0_en","features.rageshake.impl.bugreport_BugReportView_Night_0_en",20161,], +["features.rageshake.impl.bugreport_BugReportView_Day_1_en","features.rageshake.impl.bugreport_BugReportView_Night_1_en",20161,], +["features.rageshake.impl.bugreport_BugReportView_Day_2_en","features.rageshake.impl.bugreport_BugReportView_Night_2_en",20161,], +["features.rageshake.impl.bugreport_BugReportView_Day_3_en","features.rageshake.impl.bugreport_BugReportView_Night_3_en",20161,], +["features.rageshake.impl.bugreport_BugReportView_Day_4_en","features.rageshake.impl.bugreport_BugReportView_Night_4_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20147,], +["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",20161,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20161,], ["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_0_en","features.call.impl.ui_CallScreenPipView_Night_0_en",0,], ["features.call.impl.ui_CallScreenPipView_Day_1_en","features.call.impl.ui_CallScreenPipView_Night_1_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",20147,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20147,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20147,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20147,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20147,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20147,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20161,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20161,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20161,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20161,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_10_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_10_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_1_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_1_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_2_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_2_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_3_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_3_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_4_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_4_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_5_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_5_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_6_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_6_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_7_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_7_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_8_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_8_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Day_9_en","features.roomdetails.impl.rolesandpermissions.changeroles_ChangeRolesView_Night_9_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20161,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20161,], ["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",20147,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20147,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20161,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20161,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20147,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20161,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20164,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20164,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20164,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20164,], ["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",20147,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20147,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20147,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20161,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20161,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20161,], ["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,], -["libraries.textcomposer.components_ComposerOptionsButton_Day_0_en","libraries.textcomposer.components_ComposerOptionsButton_Night_0_en",0,], ["libraries.designsystem.components.avatar_CompositeAvatar_Avatars_en","",0,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20147,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20147,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20147,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20147,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20147,], -["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20147,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20161,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20161,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20161,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20161,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20161,], +["features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20161,], ["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",20147,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20147,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20147,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20147,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20147,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20147,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20147,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20147,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20147,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20147,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20147,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20147,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20147,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20147,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20147,], -["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20147,], -["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20147,], -["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20147,], -["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20147,], -["features.createroom.impl.root_CreateRoomRootView_Day_4_en","features.createroom.impl.root_CreateRoomRootView_Night_4_en",20147,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20147,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20147,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20147,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20147,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20147,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20161,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20161,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20161,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20161,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20161,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20161,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20161,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20161,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20161,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20161,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20161,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20161,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20161,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20161,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_0_en","features.createroom.impl.root_CreateRoomRootView_Night_0_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_1_en","features.createroom.impl.root_CreateRoomRootView_Night_1_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_2_en","features.createroom.impl.root_CreateRoomRootView_Night_2_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_3_en","features.createroom.impl.root_CreateRoomRootView_Night_3_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_4_en","features.createroom.impl.root_CreateRoomRootView_Night_4_en",20161,], +["features.createroom.impl.root_CreateRoomRootView_Day_5_en","features.createroom.impl.root_CreateRoomRootView_Night_5_en",20161,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20161,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20161,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20161,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20161,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20161,], ["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","",20147,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20147,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20161,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20161,], ["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",20147,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20147,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20147,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20161,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20161,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20161,], ["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",20147,], -["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20147,], -["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20147,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20161,], +["features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBarWithIndicator_Night_0_en",20161,], +["features.roomlist.impl.components_DefaultRoomListTopBar_Day_0_en","features.roomlist.impl.components_DefaultRoomListTopBar_Night_0_en",20161,], ["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",20147,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20147,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20147,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20147,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20147,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20147,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20147,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20161,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20161,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20161,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20161,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20161,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20161,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20161,], ["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,], ["libraries.designsystem.theme.components_DialogWithTitleAndOkButton_Dialog_with_title_and_ok_button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithTitleIconAndOkButton_Dialog_with_title,_icon_and_ok_button_Dialogs_en","",0,], ["features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en","features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en",0,], -["libraries.textcomposer.components_DismissTextFormattingButton_Day_0_en","libraries.textcomposer.components_DismissTextFormattingButton_Night_0_en",0,], ["libraries.designsystem.components.avatar_DmAvatarsRtl_Avatars_en","",0,], ["libraries.designsystem.components.avatar_DmAvatars_Avatars_en","",0,], ["libraries.designsystem.text_DpScale_0_75f__en","",0,], ["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",20147,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20147,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20147,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20147,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20147,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20147,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20147,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20147,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20147,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20147,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20147,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20161,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20161,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20161,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20161,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20161,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20161,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20161,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20161,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20161,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20161,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20161,], ["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,], @@ -350,9 +357,9 @@ export const screenshots = [ ["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_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiPicker_Night_0_en",0,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20147,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20147,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20147,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20161,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20161,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20161,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_0_en",0,], @@ -370,15 +377,16 @@ 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",20147,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20147,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20147,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20161,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20161,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20161,], +["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",20147,], -["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20147,], +["features.messages.impl.forward_ForwardMessagesView_Day_3_en","features.messages.impl.forward_ForwardMessagesView_Night_3_en",20161,], +["features.roomlist.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.roomlist.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20161,], ["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,], @@ -386,6 +394,7 @@ export const screenshots = [ ["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,], +["libraries.designsystem.theme.components_IconColorButton_Day_0_en","libraries.designsystem.theme.components_IconColorButton_Night_0_en",0,], ["libraries.designsystem.theme.components_IconImageVector_Icons_en","",0,], ["libraries.designsystem.atomic.molecules_IconTitlePlaceholdersRowMolecule_Day_0_en","libraries.designsystem.atomic.molecules_IconTitlePlaceholdersRowMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.molecules_IconTitleSubtitleMolecule_Day_0_en","libraries.designsystem.atomic.molecules_IconTitleSubtitleMolecule_Night_0_en",0,], @@ -400,8 +409,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",20147,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20147,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20161,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20161,], ["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,], @@ -409,74 +418,80 @@ 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",20147,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20161,], ["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",20147,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20161,], ["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",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20147,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20147,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20164,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20164,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20164,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20164,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20161,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20164,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20164,], ["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.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20147,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20150,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20150,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20150,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20150,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20150,], -["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20150,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20161,], +["features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.createroom.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20161,], ["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",20147,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20147,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20147,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20147,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20147,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20161,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20161,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20161,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20161,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], ["features.leaveroom.api_LeaveRoomView_Day_0_en","features.leaveroom.api_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",20147,], -["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20147,], -["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20147,], -["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20147,], -["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20147,], -["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20147,], +["features.leaveroom.api_LeaveRoomView_Day_1_en","features.leaveroom.api_LeaveRoomView_Night_1_en",20161,], +["features.leaveroom.api_LeaveRoomView_Day_2_en","features.leaveroom.api_LeaveRoomView_Night_2_en",20161,], +["features.leaveroom.api_LeaveRoomView_Day_3_en","features.leaveroom.api_LeaveRoomView_Night_3_en",20161,], +["features.leaveroom.api_LeaveRoomView_Day_4_en","features.leaveroom.api_LeaveRoomView_Night_4_en",20161,], +["features.leaveroom.api_LeaveRoomView_Day_5_en","features.leaveroom.api_LeaveRoomView_Night_5_en",20161,], +["features.leaveroom.api_LeaveRoomView_Day_6_en","features.leaveroom.api_LeaveRoomView_Night_6_en",20161,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], @@ -533,29 +548,29 @@ 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",20147,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20147,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20147,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20147,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20161,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20161,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20161,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20161,], ["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",20147,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20147,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20147,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20147,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20147,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20147,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20147,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20147,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20147,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20147,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20147,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20147,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20147,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20147,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20147,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20147,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20161,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20161,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20161,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20161,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20161,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20161,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20161,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20161,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20161,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20161,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20161,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20161,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20161,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20161,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20161,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20161,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20147,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20161,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNeutral_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNeutral_Night_0_en",0,], @@ -567,22 +582,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",20147,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20147,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20161,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20161,], ["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",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20147,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20147,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20161,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20161,], ["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,], @@ -590,13 +605,13 @@ 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","",20147,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20147,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20161,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20161,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20147,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20161,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20147,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20161,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -608,7 +623,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",20147,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20161,], ["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_10_en","features.messages.impl.timeline.components_MessageEventBubble_Night_10_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_11_en","features.messages.impl.timeline.components_MessageEventBubble_Night_11_en",0,], @@ -625,7 +640,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_8_en","features.messages.impl.timeline.components_MessageEventBubble_Night_8_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_9_en","features.messages.impl.timeline.components_MessageEventBubble_Night_9_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20147,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20161,], ["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,], @@ -633,23 +648,24 @@ 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",20147,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20147,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20147,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20147,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20147,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20147,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20147,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20147,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20147,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20147,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20147,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20147,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20147,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20147,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20147,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20161,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20161,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20161,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20161,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20161,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20161,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20164,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20161,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20161,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20161,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20161,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20161,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20161,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20161,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20161,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20161,], ["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",20147,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20161,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], @@ -658,28 +674,28 @@ export const screenshots = [ ["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,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20150,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20147,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20147,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20147,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20161,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20161,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20161,], ["libraries.oidc.impl.webview_OidcView_Day_0_en","libraries.oidc.impl.webview_OidcView_Night_0_en",0,], ["libraries.oidc.impl.webview_OidcView_Day_1_en","libraries.oidc.impl.webview_OidcView_Night_1_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",20147,], -["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20147,], -["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20147,], -["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20147,], +["features.onboarding.impl_OnBoardingView_Day_0_en","features.onboarding.impl_OnBoardingView_Night_0_en",20161,], +["features.onboarding.impl_OnBoardingView_Day_1_en","features.onboarding.impl_OnBoardingView_Night_1_en",20161,], +["features.onboarding.impl_OnBoardingView_Day_2_en","features.onboarding.impl_OnBoardingView_Night_2_en",20161,], +["features.onboarding.impl_OnBoardingView_Day_3_en","features.onboarding.impl_OnBoardingView_Night_3_en",20161,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], @@ -692,66 +708,67 @@ export const screenshots = [ ["libraries.designsystem.components_PageTitleWithIconFull_Day_3_en","libraries.designsystem.components_PageTitleWithIconFull_Night_3_en",0,], ["libraries.designsystem.components_PageTitleWithIconFull_Day_4_en","libraries.designsystem.components_PageTitleWithIconFull_Night_4_en",0,], ["libraries.designsystem.components_PageTitleWithIconFull_Day_5_en","libraries.designsystem.components_PageTitleWithIconFull_Night_5_en",0,], +["libraries.designsystem.components_PageTitleWithIconFull_Day_6_en","libraries.designsystem.components_PageTitleWithIconFull_Night_6_en",0,], ["libraries.designsystem.components_PageTitleWithIconMinimal_Day_0_en","libraries.designsystem.components_PageTitleWithIconMinimal_Night_0_en",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20147,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20147,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20147,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20147,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20147,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Day_0_en","features.roomdetails.impl.rolesandpermissions.changeroles_PendingMemberRowWithLongName_Night_0_en",20161,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20161,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20161,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20161,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20161,], ["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",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20147,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20147,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20161,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20161,], ["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",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20147,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20147,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20147,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20147,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20147,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20147,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20161,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20161,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20161,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20161,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20161,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20161,], ["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",20147,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20147,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20147,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20147,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20147,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20161,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20161,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20161,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20161,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20161,], ["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",20147,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20147,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20147,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20147,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20147,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20147,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20147,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20147,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20147,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20147,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20147,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20161,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20161,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20161,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20161,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20161,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20161,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20161,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20161,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20161,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20161,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20161,], ["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,], @@ -765,205 +782,207 @@ 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,], -["libraries.designsystem.components.preferences_PreferenceTextDark_Preferences_en","",0,], -["libraries.designsystem.components.preferences_PreferenceTextLight_Preferences_en","",0,], -["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_Preferences_en","",0,], -["libraries.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20147,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20147,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20147,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20147,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20161,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20161,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20161,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20161,], ["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","",20147,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20147,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20147,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20147,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20147,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20147,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20147,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20147,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20147,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20147,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20147,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20147,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20147,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20147,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20147,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20147,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20161,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20161,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20161,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20161,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20161,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20161,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20161,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20161,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20161,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20161,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20161,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20161,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20161,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20161,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20161,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20161,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20147,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20147,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20161,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20147,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20147,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20147,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20147,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20147,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20147,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20161,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20161,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20161,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20161,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20161,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20161,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20161,], ["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",20147,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20147,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20147,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20147,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20147,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20147,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20147,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20147,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20147,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20147,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20147,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20147,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20161,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20161,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20161,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20161,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20161,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20161,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20161,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20161,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20161,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20161,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20161,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20161,], ["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",20147,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20147,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20147,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20147,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20147,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20161,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20161,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20161,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20161,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20161,], ["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",20147,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20147,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20147,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20147,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20147,], -["features.roomdetails.impl_RoomDetails_0_en","",20147,], -["features.roomdetails.impl_RoomDetails_10_en","",20147,], -["features.roomdetails.impl_RoomDetails_11_en","",20147,], -["features.roomdetails.impl_RoomDetails_12_en","",20147,], -["features.roomdetails.impl_RoomDetails_13_en","",20147,], -["features.roomdetails.impl_RoomDetails_14_en","",20147,], -["features.roomdetails.impl_RoomDetails_15_en","",20147,], -["features.roomdetails.impl_RoomDetails_1_en","",20147,], -["features.roomdetails.impl_RoomDetails_2_en","",20147,], -["features.roomdetails.impl_RoomDetails_3_en","",20147,], -["features.roomdetails.impl_RoomDetails_4_en","",20147,], -["features.roomdetails.impl_RoomDetails_5_en","",20147,], -["features.roomdetails.impl_RoomDetails_6_en","",20147,], -["features.roomdetails.impl_RoomDetails_7_en","",20147,], -["features.roomdetails.impl_RoomDetails_8_en","",20147,], -["features.roomdetails.impl_RoomDetails_9_en","",20147,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20147,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20147,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20147,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20147,], -["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20147,], -["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20147,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20161,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20161,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20164,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20164,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20164,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20161,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20161,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20161,], +["features.roomdetails.impl_RoomDetails_0_en","",20161,], +["features.roomdetails.impl_RoomDetails_10_en","",20161,], +["features.roomdetails.impl_RoomDetails_11_en","",20161,], +["features.roomdetails.impl_RoomDetails_12_en","",20161,], +["features.roomdetails.impl_RoomDetails_13_en","",20161,], +["features.roomdetails.impl_RoomDetails_14_en","",20161,], +["features.roomdetails.impl_RoomDetails_15_en","",20161,], +["features.roomdetails.impl_RoomDetails_16_en","",20164,], +["features.roomdetails.impl_RoomDetails_17_en","",20164,], +["features.roomdetails.impl_RoomDetails_18_en","",20164,], +["features.roomdetails.impl_RoomDetails_1_en","",20161,], +["features.roomdetails.impl_RoomDetails_2_en","",20161,], +["features.roomdetails.impl_RoomDetails_3_en","",20161,], +["features.roomdetails.impl_RoomDetails_4_en","",20161,], +["features.roomdetails.impl_RoomDetails_5_en","",20161,], +["features.roomdetails.impl_RoomDetails_6_en","",20161,], +["features.roomdetails.impl_RoomDetails_7_en","",20161,], +["features.roomdetails.impl_RoomDetails_8_en","",20161,], +["features.roomdetails.impl_RoomDetails_9_en","",20161,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20161,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20161,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_4_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_4_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_5_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_5_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_6_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_6_en",20161,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_7_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_7_en",20161,], +["features.roomlist.impl.components_RoomListContentView_Day_0_en","features.roomlist.impl.components_RoomListContentView_Night_0_en",20161,], +["features.roomlist.impl.components_RoomListContentView_Day_1_en","features.roomlist.impl.components_RoomListContentView_Night_1_en",20161,], ["features.roomlist.impl.components_RoomListContentView_Day_2_en","features.roomlist.impl.components_RoomListContentView_Night_2_en",0,], -["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",20147,], -["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20147,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20147,], -["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20147,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20147,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20147,], -["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20147,], +["features.roomlist.impl.components_RoomListContentView_Day_3_en","features.roomlist.impl.components_RoomListContentView_Night_3_en",20161,], +["features.roomlist.impl.components_RoomListContentView_Day_4_en","features.roomlist.impl.components_RoomListContentView_Night_4_en",20161,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_0_en","features.roomlist.impl.filters_RoomListFiltersView_Night_0_en",20161,], +["features.roomlist.impl.filters_RoomListFiltersView_Day_1_en","features.roomlist.impl.filters_RoomListFiltersView_Night_1_en",20161,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_0_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_0_en",20161,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_1_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_1_en",20161,], +["features.roomlist.impl_RoomListModalBottomSheetContent_Day_2_en","features.roomlist.impl_RoomListModalBottomSheetContent_Night_2_en",20161,], ["features.roomlist.impl.search_RoomListSearchContent_Day_0_en","features.roomlist.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",20147,], -["features.roomlist.impl.search_RoomListSearchContent_Day_2_en","features.roomlist.impl.search_RoomListSearchContent_Night_2_en",20147,], -["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20147,], -["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20147,], -["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20147,], -["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20147,], -["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20147,], -["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20147,], -["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20147,], -["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20147,], -["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20147,], +["features.roomlist.impl.search_RoomListSearchContent_Day_1_en","features.roomlist.impl.search_RoomListSearchContent_Night_1_en",20161,], +["features.roomlist.impl_RoomListView_Day_0_en","features.roomlist.impl_RoomListView_Night_0_en",20161,], +["features.roomlist.impl_RoomListView_Day_10_en","features.roomlist.impl_RoomListView_Night_10_en",20161,], +["features.roomlist.impl_RoomListView_Day_1_en","features.roomlist.impl_RoomListView_Night_1_en",20161,], +["features.roomlist.impl_RoomListView_Day_2_en","features.roomlist.impl_RoomListView_Night_2_en",20161,], +["features.roomlist.impl_RoomListView_Day_3_en","features.roomlist.impl_RoomListView_Night_3_en",20161,], +["features.roomlist.impl_RoomListView_Day_4_en","features.roomlist.impl_RoomListView_Night_4_en",20161,], +["features.roomlist.impl_RoomListView_Day_5_en","features.roomlist.impl_RoomListView_Night_5_en",20161,], +["features.roomlist.impl_RoomListView_Day_6_en","features.roomlist.impl_RoomListView_Night_6_en",20161,], +["features.roomlist.impl_RoomListView_Day_7_en","features.roomlist.impl_RoomListView_Night_7_en",20161,], ["features.roomlist.impl_RoomListView_Day_8_en","features.roomlist.impl_RoomListView_Night_8_en",0,], ["features.roomlist.impl_RoomListView_Day_9_en","features.roomlist.impl_RoomListView_Night_9_en",0,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20147,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20147,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",0,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20147,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20147,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20161,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20161,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20164,], +["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",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20161,], +["features.roomdetails.impl.members_RoomMemberListView_Day_9_en","features.roomdetails.impl.members_RoomMemberListView_Night_9_en",20164,], ["libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Day_0_en","libraries.designsystem.atomic.molecules_RoomMembersCountMolecule_Night_0_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20147,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_0_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_0_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_1_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_1_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_2_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_2_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_3_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_3_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_4_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_4_en",20161,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_5_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_5_en",0,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20147,], -["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20147,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_6_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_6_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_7_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_7_en",20161,], +["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_8_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_8_en",20161,], ["features.roomdetails.impl.members.moderation_RoomMembersModerationView_Day_9_en","features.roomdetails.impl.members.moderation_RoomMembersModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20147,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20147,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20147,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20161,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20161,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20161,], ["features.roomlist.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.roomlist.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_0_en","features.roomlist.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_10_en","features.roomlist.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -986,12 +1005,12 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_26_en","features.roomlist.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_27_en","features.roomlist.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_28_en","features.roomlist.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",20147,], -["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20147,], -["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20147,], -["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20147,], -["features.roomlist.impl.components_RoomSummaryRow_Day_32_en","features.roomlist.impl.components_RoomSummaryRow_Night_32_en",20147,], -["features.roomlist.impl.components_RoomSummaryRow_Day_33_en","features.roomlist.impl.components_RoomSummaryRow_Night_33_en",20147,], +["features.roomlist.impl.components_RoomSummaryRow_Day_29_en","features.roomlist.impl.components_RoomSummaryRow_Night_29_en",20161,], +["features.roomlist.impl.components_RoomSummaryRow_Day_2_en","features.roomlist.impl.components_RoomSummaryRow_Night_2_en",20161,], +["features.roomlist.impl.components_RoomSummaryRow_Day_30_en","features.roomlist.impl.components_RoomSummaryRow_Night_30_en",20161,], +["features.roomlist.impl.components_RoomSummaryRow_Day_31_en","features.roomlist.impl.components_RoomSummaryRow_Night_31_en",20161,], +["features.roomlist.impl.components_RoomSummaryRow_Day_32_en","features.roomlist.impl.components_RoomSummaryRow_Night_32_en",20161,], +["features.roomlist.impl.components_RoomSummaryRow_Day_33_en","features.roomlist.impl.components_RoomSummaryRow_Night_33_en",20161,], ["features.roomlist.impl.components_RoomSummaryRow_Day_3_en","features.roomlist.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_4_en","features.roomlist.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_5_en","features.roomlist.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -999,77 +1018,77 @@ export const screenshots = [ ["features.roomlist.impl.components_RoomSummaryRow_Day_7_en","features.roomlist.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_8_en","features.roomlist.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.roomlist.impl.components_RoomSummaryRow_Day_9_en","features.roomlist.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20147,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20147,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20147,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20161,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20161,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20161,], ["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",20147,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20147,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20147,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20161,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20161,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20161,], ["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","",20147,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20161,], ["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.createroom.impl.components_SearchMultipleUsersResultItem_en","",20147,], -["features.createroom.impl.components_SearchSingleUserResultItem_en","",20147,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20147,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20147,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20147,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20147,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20147,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20147,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20147,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20147,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20147,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20147,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20147,], +["features.createroom.impl.components_SearchMultipleUsersResultItem_en","",20161,], +["features.createroom.impl.components_SearchSingleUserResultItem_en","",20161,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20161,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20161,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20161,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20161,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20161,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20161,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20161,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20161,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20161,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20161,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20161,], ["libraries.matrix.ui.components_SelectedRoom_Day_0_en","libraries.matrix.ui.components_SelectedRoom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_1_en","libraries.matrix.ui.components_SelectedRoom_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedRoom_Day_2_en","libraries.matrix.ui.components_SelectedRoom_Night_2_en",0,], @@ -1077,11 +1096,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_SelectedUser_Day_0_en","libraries.matrix.ui.components_SelectedUser_Night_0_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",20147,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20147,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20147,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20147,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20147,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20161,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20161,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20161,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20161,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20161,], ["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,], @@ -1091,27 +1110,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",20147,], -["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20147,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20147,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20147,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20147,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20147,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20147,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20147,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20161,], +["features.roomlist.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.roomlist.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20161,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20161,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20161,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20161,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20161,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20161,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20161,], ["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",20147,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20147,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20147,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20147,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20147,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20147,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20147,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20147,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20147,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20147,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20161,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20161,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20161,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20161,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20161,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20161,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20161,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20161,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20161,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20161,], ["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,], @@ -1120,7 +1139,7 @@ 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",20147,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20161,], ["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,], @@ -1130,40 +1149,40 @@ export const screenshots = [ ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], ["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",0,], -["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",20147,], +["features.location.api.internal_StaticMapPlaceholder_Day_1_en","features.location.api.internal_StaticMapPlaceholder_Night_1_en",20161,], ["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",20147,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20161,], ["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",20147,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20161,], ["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",20147,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20147,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20147,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20147,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20147,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20147,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20147,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20147,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20147,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20147,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20161,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20161,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20161,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20161,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20161,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20161,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20161,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20161,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20161,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20161,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], @@ -1173,14 +1192,14 @@ export const screenshots = [ ["libraries.designsystem.theme.components_TextFieldsLight_TextFields_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","",20147,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20147,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20147,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20161,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20161,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20161,], ["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",20147,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20147,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20161,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20161,], ["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.event_TimelineImageWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineImageWithCaptionRow_Night_0_en",0,], @@ -1189,18 +1208,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",20147,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20147,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20161,], ["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,], @@ -1208,17 +1227,17 @@ 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",20147,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20147,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20161,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20161,], ["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_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20147,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20147,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20161,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20147,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20161,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20161,], ["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,], @@ -1227,40 +1246,40 @@ 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",20147,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20161,], ["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",20147,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20161,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], ["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","",20147,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20161,], ["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",20147,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20147,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20161,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20161,], ["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",20147,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20147,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20147,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20147,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20161,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20147,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20161,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20161,], ["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",20147,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20161,], ["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,], @@ -1269,8 +1288,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",20147,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20147,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20161,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20161,], ["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,], @@ -1285,8 +1304,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",20147,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20147,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20161,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20161,], ["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,], @@ -1309,85 +1328,88 @@ 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",20147,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20147,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20161,], ["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",20147,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20147,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20147,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20161,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20161,], ["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",20147,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20161,], ["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",20147,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20161,], ["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",20147,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20161,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_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",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20147,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20147,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20161,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20161,], ["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",20147,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20147,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20147,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20147,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20147,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20147,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20161,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20161,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20161,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20161,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20161,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20161,], ["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","",20147,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20161,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20147,], -["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20147,], -["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20147,], -["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20147,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20161,], +["features.createroom.impl.components_UserListView_Day_0_en","features.createroom.impl.components_UserListView_Night_0_en",20161,], +["features.createroom.impl.components_UserListView_Day_1_en","features.createroom.impl.components_UserListView_Night_1_en",20161,], +["features.createroom.impl.components_UserListView_Day_2_en","features.createroom.impl.components_UserListView_Night_2_en",20161,], ["features.createroom.impl.components_UserListView_Day_3_en","features.createroom.impl.components_UserListView_Night_3_en",0,], ["features.createroom.impl.components_UserListView_Day_4_en","features.createroom.impl.components_UserListView_Night_4_en",0,], ["features.createroom.impl.components_UserListView_Day_5_en","features.createroom.impl.components_UserListView_Night_5_en",0,], ["features.createroom.impl.components_UserListView_Day_6_en","features.createroom.impl.components_UserListView_Night_6_en",0,], -["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",20147,], +["features.createroom.impl.components_UserListView_Day_7_en","features.createroom.impl.components_UserListView_Night_7_en",20161,], ["features.createroom.impl.components_UserListView_Day_8_en","features.createroom.impl.components_UserListView_Night_8_en",0,], -["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",20147,], +["features.createroom.impl.components_UserListView_Day_9_en","features.createroom.impl.components_UserListView_Night_9_en",20161,], ["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_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20147,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20147,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20147,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20147,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20147,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20147,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20147,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20147,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20147,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en",0,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20164,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20161,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20161,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20161,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20161,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20161,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20161,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20161,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20161,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20161,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20161,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20164,], +["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en",20164,], ["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_12_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_12_en",0,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en",20147,], -["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en",20147,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en",0,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en",20161,], +["features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en","features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en",20161,], ["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,], @@ -1413,6 +1435,6 @@ export const screenshots = [ ["libraries.textcomposer.components_VoiceMessageRecording_Day_0_en","libraries.textcomposer.components_VoiceMessageRecording_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessage_Day_0_en","libraries.textcomposer.components_VoiceMessage_Night_0_en",0,], ["libraries.designsystem.components.media_WaveformPlaybackView_Day_0_en","libraries.designsystem.components.media_WaveformPlaybackView_Night_0_en",0,], -["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",20147,], +["features.ftue.impl.welcome_WelcomeView_Day_0_en","features.ftue.impl.welcome_WelcomeView_Night_0_en",20161,], ["libraries.designsystem.ruler_WithRulers_Day_0_en","libraries.designsystem.ruler_WithRulers_Night_0_en",0,], ]; 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 b5e75b904d..020245b906 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 @@ -51,6 +51,7 @@ class KonsistClassNameTest { .withoutName( "AspectRatioProvider", "OverlapRatioProvider", + "TextFileContentProvider", ) .also { // Check that classes are actually found 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 e1ebdba139..ed17a0a3da 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 @@ -66,7 +66,9 @@ class KonsistPreviewTest { "CallScreenPipViewPreview", "ColorAliasesPreview", "DefaultRoomListTopBarWithIndicatorPreview", + "FocusedEventPreview", "GradientFloatingActionButtonCircleShapePreview", + "HeaderFooterPageScrollablePreview", "IconsCompoundPreview", "IconsOtherPreview", "MarkdownTextComposerEditPreview", @@ -128,6 +130,7 @@ class KonsistPreviewTest { "TimelineVideoWithCaptionRowPreview", "TimelineViewMessageShieldPreview", "UserAvatarColorsPreview", + "UserProfileHeaderSectionWithVerificationViolationPreview", "VoiceItemViewPlayPreview", ) .assertTrue( diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt index 689a52c792..fe45364f49 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/EnsureNeverCalled.kt @@ -38,3 +38,9 @@ class EnsureNeverCalledWithTwoParamsAndResult : (T, U) -> R { lambdaError("Should not be called and is called with $p1 and $p2") } } + +class EnsureNeverCalledWithThreeParams : (T, U, V) -> Unit { + override fun invoke(p1: T, p2: U, p3: V) { + lambdaError("Should not be called and is called with $p1, $p2 and $p3") + } +} diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WithFakeLifecycleOwner.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WithFakeLifecycleOwner.kt index caafe14dc2..16d4cfb2fd 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WithFakeLifecycleOwner.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/WithFakeLifecycleOwner.kt @@ -9,31 +9,64 @@ package io.element.android.tests.testutils import android.annotation.SuppressLint import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.InternalComposeApi import androidx.compose.runtime.Stable +import androidx.compose.runtime.currentComposer import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.compose.LocalLifecycleOwner +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.TurbineTestContext +import app.cash.turbine.test +import io.element.android.libraries.architecture.Presenter +/** + * Composable that provides a fake [LifecycleOwner] to the composition. + * + * **WARNING: DO NOT USE OUTSIDE TESTS.** + */ +@OptIn(InternalComposeApi::class) @Stable @Composable -fun withFakeLifecycleOwner(lifecycleOwner: FakeLifecycleOwner = FakeLifecycleOwner(), block: @Composable () -> T): T { - var state: T? by remember { mutableStateOf(null) } - CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) { - state = block() - } - return state!! +fun withFakeLifecycleOwner( + lifecycleOwner: FakeLifecycleOwner = FakeLifecycleOwner(), + block: @Composable () -> T +): T { + currentComposer.startProvider(LocalLifecycleOwner provides lifecycleOwner) + val state = block() + currentComposer.endProvider() + return state +} + +/** + * Test a [Presenter] with a fake [LifecycleOwner]. + * + * **WARNING: DO NOT USE OUTSIDE TESTS.** + */ +suspend fun Presenter.testWithLifecycleOwner( + lifecycleOwner: FakeLifecycleOwner = FakeLifecycleOwner(), + block: suspend TurbineTestContext.() -> Unit +) { + moleculeFlow(RecompositionMode.Immediate) { + val ret = withFakeLifecycleOwner(lifecycleOwner) { + present() + } + ret + }.test(validate = block) } @SuppressLint("VisibleForTests") -class FakeLifecycleOwner : LifecycleOwner { +class FakeLifecycleOwner(initialState: Lifecycle.State? = null) : LifecycleOwner { override val lifecycle: Lifecycle = LifecycleRegistry.createUnsafe(this) + init { + initialState?.let { givenState(it) } + } + fun givenState(state: Lifecycle.State) { (lifecycle as LifecycleRegistry).currentState = state } diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en.png new file mode 100644 index 0000000000..2dd4cdac47 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:470cb7c851af9d91cc44d52d502b50764136b079b600ca58ae2512e7ff12e34b +size 29534 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en.png new file mode 100644 index 0000000000..08cde67f54 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49db4684bd8f16361848130650ce1ee0fbc49fff5452fab177e5920ab857c1a9 +size 24663 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en.png new file mode 100644 index 0000000000..7e53aff661 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1504a5137b90bf6c03ae8d3a90946ae1a9b528013f9c3898a0e91e4d073d159 +size 34347 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en.png new file mode 100644 index 0000000000..6736d8bf0a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd973e0f9c5940e3f216f27af682fffd156535b6d759e4cec01c3979eb35f1c9 +size 29624 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en.png new file mode 100644 index 0000000000..c6692886f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9dba1ef09211361216ee035c64ee50d62ab1df5191d365ef26f5a4f741477867 +size 28635 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en.png new file mode 100644 index 0000000000..cdb70c0358 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba7502251d19d1fe54fa631b7c71afcdfc3c864cbab04d741a0c4a8d691b2e7f +size 24048 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en.png new file mode 100644 index 0000000000..7a11690369 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14770fdc3e68b250b5f7dad6bb6fb1bf387c66774287276ab4b505c144007b33 +size 33299 diff --git a/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en.png new file mode 100644 index 0000000000..86c1142a7a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0466b1f78eda1575072f9be443fcb5741ff9952fd215b9aa9b2281de7109da70 +size 28783 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png index 4464e8ba31..9009f71917 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc7f78c0ce55c6e1b0759576f80511d7718291e5903cd0a7cbb19c46bfbec686 -size 17684 +oid sha256:768def050ec8d603c718a9e7732b62beb89a659d34b4b41046cecb08459c68a6 +size 17603 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png index 10f33937a8..e82405c849 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfc723cc3cadef302fae4db56142e7a4ecdec4c43593e0d1122a8b3bb8d4770 -size 7616 +oid sha256:03df86605a73df3692a163067cdca41048e13fb769f9ed5de9c54817976b0a03 +size 7530 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png index ea0ab6bd30..179e480111 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5fb195657c1ac0379839bba6acc0935f6b1b946714ba252d5797fd4bc0c514db -size 6753 +oid sha256:aef1852325b4a0539aaf3d7667fbe84bd32bd8e0018eb1678cc53a99c6cbc746 +size 6660 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png index 61b321d08a..7248bdc8ec 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18e3bb4c57bddd1422c68ddf3a02be0d948e21c55857efb33f71fd4f0039d37c -size 17408 +oid sha256:de83329e8f9f0720eab60d4f89b97a99924b5504eca00f1665fa0cb252b89202 +size 17177 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png index 0c05554686..d90b063ee7 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c100f4c1d3d7c33085b76306823c4050121534ff096a9d73124683ff8db6090 -size 7541 +oid sha256:79b50e11185dcdf0810d8f46cd83ff9e2de313524d3e889343435d4a1eefe16a +size 7340 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png index 20f4afd272..2550777c95 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.accountprovider_AccountProviderView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec5654de38a98ee4b078aa3dfa093a686cb89a2f200eb69f59e78041eb8b2f60 -size 6743 +oid sha256:2347feacb167ede5eec2e26a8372bab7e5fa3953e6703918fac50c31561a353a +size 6557 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en.png index 8ca98d0073..c76898c457 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3579338105d8e01fdf3f84aabb68e63e9bafd2b7ddabd3995a3be11f3b3672dc -size 47408 +oid sha256:c5d38292c3927910a4ac9df5d2ad63dddf41818611096db2ba4a2101b880c8c0 +size 47316 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en.png index b56ea868dc..81774f463f 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:292e2308c0bee2f6cde0db70a25014be2ebe68fa2818bf6bbfbb2a4db4ab522f -size 46542 +oid sha256:4a954fc1d3091b50581dcb4cdd9ca89acb0c9c92e35fc9ffd6bfe240685d37b0 +size 46333 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en.png index c84b993cd2..469acb982f 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fcad66725dfbd432d4dc62d5d2464432496db0ff1b76756292da93486dec7fe -size 51518 +oid sha256:c5ac88572a7fbcacbf571b86586a887ec4931c9e92c15f2ac3633e0adc4088e4 +size 51364 diff --git a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en.png index 206a212fcf..751f19d89c 100644 --- a/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f8f3e8071ee6403cb8b27e6c17f35628bf2152f0fa878513c871a4393971e05 -size 50518 +oid sha256:02f1214c9ced09fcb178d04ec0e8683ecb1f31c169056db9a3849e6680d060fe +size 50318 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png index 120bb7e6ca..e4e07f7992 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6e745d85860818603552ca994df8cd214533ce97588dc92c67a947fb0b1a553 -size 54985 +oid sha256:ec8ac8528bdb81076828f1b3a87b968ca41b21e59b6ac9c2b5c28db969a08952 +size 54998 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png index d37dacfa04..42cbefd3ee 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:229981835f9ac9137363e5c70d8ed346c9553f4b0ddb673c7d4d42e2ee8732c2 +oid sha256:5ccc78d23a4abbbae42cb433fc654e63ed1a3b92c5ffad25b784176a272ff95c size 65047 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png index 32dd1a0fa4..d8be159c65 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49d994e3c100153c0cfe39b4ac839409276456ec3ef3d1f23b56d4976ba69110 -size 68309 +oid sha256:98b454bc6221fd8c49ebea5ad56f61068a2c4cde74b3d0f1c0525087c19628f5 +size 65685 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png index 86fba55e9a..f3c3b76e87 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b2d33ce0fa59fa4d3b411047cd3d6291deda7e560ddbe58139f84d1319fc2a8 -size 55394 +oid sha256:0abcce8207b0f2ff7690e80a64a377eefa9aacb3a2c49ea7d22b452c117d6ce0 +size 55455 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png index c7c77df34d..868634be17 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7abc8d2e9fa899bff932685ab34221b4c2bba50d5decb56df053122b39d003f2 -size 69118 +oid sha256:edcb3bf7ecb7002a208ecc3655bd54bf2c998ea82dbd757cda1f4c6eb0f2b8a6 +size 69168 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png index b4ad30238a..86091e0911 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a33db712beeead338bd88e964c2319646dcb72bdc3f7dac4d1d2f1e322990ba5 -size 70576 +oid sha256:e388d6d9cfddaf48d83ee50b09c17a44673830a0e18ec97335acbf454c4ceb32 +size 68924 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en.png index 205952091d..3ab803e484 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d866ee561b4109bf1bb4d1cc3b34d32ad0c67ae51f7599088898acd4310f961 -size 6725 +oid sha256:fac7865222fa43b756f71af4203fa8c1840caa2e1479d6cbb66ef94fe4cef3e6 +size 6728 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en.png index a5be35d80b..f21d2053b2 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_DisabledComposerView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fcb7d90743e35ebf03f09129e02f4df7d0b3b883c451fb45f57f70669b5d4c5 -size 6382 +oid sha256:5192b4472c687b8b05e2e751bb988276f0507cf6b8d85a0e50a46d8abe9f4834 +size 6387 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Day_0_en.png index 8bf1f125bc..b2338d9962 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ceb6210817e79599a968157aebb7fd1d4e80ddbb8f3e57aabfb58d8f6066b9d4 -size 19455 +oid sha256:eb3362efdf5441cf73ac08196b95bdd5b4426e11188f6cb86c32169be049ed46 +size 19547 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Night_0_en.png index 7743b1f55a..c881726f0c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.messagecomposer_MessageComposerView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6394543bdd107f70a09bdcb164163984112a25f90eed93850cb525572dd53045 -size 17888 +oid sha256:ea025d360bedff971ae77ee3c9e47140dac75d2f8b64ea2d244304c35c8cea5c +size 18078 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en.png index 02414ad0c3..db5ba1a5ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2becc435316b7adb15745bd0afbc2634cbc643b3919b2e7405b8a2b9b310acd0 -size 4451 +oid sha256:c899d8ca8e98ae73c8acd90cef3967834901929833c4d8287356a320f2bf4c0e +size 4523 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en.png index a106ea73f7..b9cab06223 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb901534fbf1e05988710ea799d94daf9fa6a881edd84463ca0e065282ffbdb2 -size 4439 +oid sha256:f7579ab37a0ebe9671a46e591037fbd8991d3265e203d6ad0dcec6a6b7ceb588 +size 4386 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Day_0_en.png new file mode 100644 index 0000000000..8384f46c27 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2ea87ef947697fc3ea21c8953149db1c729be5391a4fd65f63b7b04aaf0e8cf +size 10465 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Night_0_en.png new file mode 100644 index 0000000000..5f1e275830 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_FocusedEvent_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ce07ba72b4542584273ee4004365fe50da4747e4aa06c46ea0d6b192647a1ac +size 10204 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en.png new file mode 100644 index 0000000000..5396163fb1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de9876d82b13a220cd296f6117bedf7c1a260d9560978701065ce068e22b1c8b +size 4485 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en.png new file mode 100644 index 0000000000..7148c0371e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5dc95cafaeda3d0cd45ce37408de0858543f1de07a29b18d165cf2c9a438e9c6 +size 4465 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en.png new file mode 100644 index 0000000000..5bcb370105 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8cf8e9260385fdc03b2bb611a7c79af93190b7f6c9cfbb53d45afebd0590d08 +size 11279 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en.png new file mode 100644 index 0000000000..d576503af4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf4558bdae3a8f6e04a3390e15194cef3faa7552f5c0fd772eb8b4a7b9954de2 +size 11234 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png index 53a292a66a..ad7f676d40 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f055dc169ef143d1523069f7cae31f2b28f5d035499ad2cf8d857151e8d7654 -size 57412 +oid sha256:ce99545c68b2d7f7f026216c33aa0f34597c0f5e371e83c4d34e3051e810f6d4 +size 57425 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png index 9025611a91..e15d4f51bf 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30b7fb609452de559ba40ab246e3edcd6f72a8bdfa1c5c9916cbac54ed89f618 -size 57444 +oid sha256:9ee0d3c122093afc601d73758400996e4d48e0193cd59c814c0cfd2cc6906070 +size 57450 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png index 5e90ff8c85..705c777dea 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b947a19d33a1ad68bb58f7a62b67b76085b1f3a544be763eddac8b8d72528a2b -size 60516 +oid sha256:b8839778513429751427a29508a5c650a8d21d88abba2c3e35c204cb871bc25c +size 60524 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png new file mode 100644 index 0000000000..f29a0f1e53 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3387b50f6e1c50831f802140d125e633c028183a26527a67b2eeb11b17b94a0 +size 60224 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png new file mode 100644 index 0000000000..4403008663 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc7b967eb01668be02cab6ce6f77994622e94cf7d44da0e6b5996d2348f43108 +size 60118 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png index 4fdb2a2e8d..38698e5432 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b956526349bacf951cfa7e5f5a74afce0c80d17bec5d7692d8c86ec92737947e -size 56578 +oid sha256:02c954978c5ddde433a96f5dd8d6cef067a558c798ad253e9034db275c5d62a2 +size 56591 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png index a1fb441a3e..32caaf5bc1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:021e70a6ef7da9dfff9666080872d899f53126a3277b06bd579fe2fe6e7a2538 -size 55214 +oid sha256:fd21bcdeeca45af5e65c5a0c57751e55ce920c10cbb30b18f04912ab7d193fda +size 55232 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png index 1d0595e4cb..67dcaa672a 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9356b6770498704389f5a065f6aaea842312dd878b6f05bb9789c56a01efd5a5 -size 55206 +oid sha256:bdc5c945b52fc5f9797f385c1eca9a7382e539ca4444bb2b94ad899e2b84585f +size 55216 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png index 60c1b9256f..b27aa9e36d 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ab021225aab0cc36b9cb01370784bb1b81828f37f5d13a847a711159f407dc0 -size 54191 +oid sha256:d2f01c4391461d28e99e2bf20af3bf48a1803d5c1d2cff537dd2c046f7c129d4 +size 54260 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png index a05435ebe6..dbabf5540c 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae5df6d7be531f90da25c7cad53a3532ef4765f071cd54c6eb3d14c0ef575358 -size 58340 +oid sha256:d52d24702141a0592ea3bf2fb61a0db7c2cbe0355645164b22fee507c9aedc0f +size 58366 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png index c5e6959c93..fa8fde5523 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b435e31a9a5a28c0e725223e3140080c24437ff557d61dd03f3e9cc39d3e34a -size 58993 +oid sha256:e54a56a22751f18b1f496fbde24872185cddde502c99c7381737fb9a215fa223 +size 59004 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png index f60b28337d..ceaf23f3ea 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1c3aea7221eaecc0ff7473bd0b8576b63a322a0a05d3b2aca6b26e16dcdc318 -size 57018 +oid sha256:8475bfc48d39dc3e553438cca9b1b088c9713774c5c16194694ef0546afacc4c +size 57060 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png index 2ec4fbb987..d2e8859001 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94835988bdfbbefe3fd60cdc616259e1b81d160a5506dd1308f7d47e6bff2113 -size 57067 +oid sha256:09ecd2bcc86a4b4571481d9414e6f467698f5f1882b05054444982bcfb0c91dc +size 57108 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png index 068a91e6cf..642558be61 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:deb926f0dbe9b5fea3c3d764b3786d43c3b7d55bc801102e9bd70b1a4c760de2 -size 59769 +oid sha256:f0f729da85f30d33ba4bb096ea11966448c1d3816fc2f845dbece8be758fcd92 +size 59816 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png new file mode 100644 index 0000000000..208d04a7b6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a0a77633d8d2bfe5f29cd772525769e4dfa613518408db8dfe8a27cf680da9f +size 59715 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png new file mode 100644 index 0000000000..88affec893 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7b1d184e7ef2e9324bd5e4b1e6b6e27521a9d9a50c12210b55594919670ee24 +size 59657 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png index d89aa791ff..04e3a8e1c1 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e2e0a5bedc5ab474af492546271b8cbe3d691bc24aab8e213cd2fc222e62507 -size 55890 +oid sha256:3023446a0e6f5c8460da144759e5e47819783f021dd88870b4335f5662077dc2 +size 55929 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png index 2232efc8f6..455893f4b9 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12fb29dd5958796433b3ac43abc06fa490a53ecd725f4f29e8745636dfbee278 -size 51221 +oid sha256:ccc2b366606bd6bc2fb3454ea2eb10539b5687232b439ce425c8f7dce9e5d92e +size 51257 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png index f09c2c0e83..b3f12adb94 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4be592eb9fc9a7654b4282f230c0189883b2b08f4c5fd4a0304951a779ba9463 -size 54783 +oid sha256:ca2d3c21d271e87a508a0ee2867550cb59c9c3a76a8ea1aa17548e364039b75d +size 54821 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png index 341042da55..6c6aa7e7f4 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:131e25f6aa90e145176f4c1a04ac3e68af8ba0bbe88fe0762e4003560ed5f187 -size 53704 +oid sha256:f8c63c180eee4972abb5a44f04cf7ec8e5b28a31ec9d1fcd9dd5fd0ce883404b +size 53773 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png index e7d8813b34..400fbd7552 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:769d3000c80c15ce0db6dac1156b7edf7d9aed79eb63e07fc05532c88f1642a3 -size 54143 +oid sha256:5577395ee585414360fcf8f9f2757ed693f9cdc1510d66758a23f7756df6f99d +size 54179 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png index f38119993b..3716d5cc03 100644 --- a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c885c63fcb4b29a22ed5db3cc8b8f8165bd3cd7856c6bf6ab431fc71b3d792e -size 58501 +oid sha256:01622d32fa8a5478d64d0d613ebffed54b3a96192ef50b4a45506e48b7200d55 +size 58546 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_1_en.png index 9627b16e6c..0d245bca19 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2350a560fcc78a3d1b22cd235746ac03b46eee2912184dbbe62683540dfee2b4 -size 11704 +oid sha256:dc12de5ab3944ab1e5ac3d352bbac286d44ed4a5e67cd040309a7a32b1cbd9d1 +size 45438 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_2_en.png index 507709b1b4..9627b16e6c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b730a39ec0ac14e8557e738dd13847b5bb1291713b0d013cf50563a1e0904f7c -size 12660 +oid sha256:2350a560fcc78a3d1b22cd235746ac03b46eee2912184dbbe62683540dfee2b4 +size 11704 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_3_en.png index 9627b16e6c..507709b1b4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2350a560fcc78a3d1b22cd235746ac03b46eee2912184dbbe62683540dfee2b4 -size 11704 +oid sha256:b730a39ec0ac14e8557e738dd13847b5bb1291713b0d013cf50563a1e0904f7c +size 12660 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_4_en.png index 92d9db2f11..9627b16e6c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d175ae95fed4ac24e9b506678c4ee1e235c3c8405915498f7f97db0764e5470c -size 7571 +oid sha256:2350a560fcc78a3d1b22cd235746ac03b46eee2912184dbbe62683540dfee2b4 +size 11704 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_5_en.png index fdb10220a2..92d9db2f11 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0db80941c8c981d3f66bc6cd9364f0f8fd5b1e7033f81da46cdffe478f97c748 -size 6528 +oid sha256:d175ae95fed4ac24e9b506678c4ee1e235c3c8405915498f7f97db0764e5470c +size 7571 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_6_en.png index 942808435a..fdb10220a2 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddb463eff5c3174663e6543726671708cef04bb11c9194b20373435e1441323a -size 24543 +oid sha256:0db80941c8c981d3f66bc6cd9364f0f8fd5b1e7033f81da46cdffe478f97c748 +size 6528 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_7_en.png index 3c8281a03c..942808435a 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57ee28833b587ec86f93c685d716f0cce94b60e0752de3fc1a1a3d7ac05cbdb6 -size 11013 +oid sha256:ddb463eff5c3174663e6543726671708cef04bb11c9194b20373435e1441323a +size 24543 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_8_en.png index abe89bd9a2..3c8281a03c 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bcc0479100b1fba092a109696b548da7a1da26583304ab7273bd454def8046b2 -size 18091 +oid sha256:57ee28833b587ec86f93c685d716f0cce94b60e0752de3fc1a1a3d7ac05cbdb6 +size 11013 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_9_en.png new file mode 100644 index 0000000000..abe89bd9a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcc0479100b1fba092a109696b548da7a1da26583304ab7273bd454def8046b2 +size 18091 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_1_en.png index 46b47ed015..43665210e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bee4a7a63485b54555a203b8e4fc557e504b3efacb91d1c7c082647848c8c45 -size 11051 +oid sha256:d24fb38f4cf8e0c8194abbbe1feca02b9515f4fe12db03f05730c2aab2a0c1e9 +size 45433 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_2_en.png index dddb67bec9..46b47ed015 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:871c608b761d8df820122d9d245ac037f71d227e7bc23f7fbe3402662ff05536 -size 11938 +oid sha256:9bee4a7a63485b54555a203b8e4fc557e504b3efacb91d1c7c082647848c8c45 +size 11051 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_3_en.png index 46b47ed015..dddb67bec9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bee4a7a63485b54555a203b8e4fc557e504b3efacb91d1c7c082647848c8c45 -size 11051 +oid sha256:871c608b761d8df820122d9d245ac037f71d227e7bc23f7fbe3402662ff05536 +size 11938 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_4_en.png index 0fbe95ff70..46b47ed015 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d174f1f8e77c9cd02506b487e410a7bcbf4d571f048f739d4f10582c72e682d0 -size 7501 +oid sha256:9bee4a7a63485b54555a203b8e4fc557e504b3efacb91d1c7c082647848c8c45 +size 11051 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_5_en.png index 228b02c0a9..0fbe95ff70 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:000c36468af4793e4956a4a2a2be1867afefa13140219e2dd8b17b1f7670b6de -size 6299 +oid sha256:d174f1f8e77c9cd02506b487e410a7bcbf4d571f048f739d4f10582c72e682d0 +size 7501 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_6_en.png index 3473f077b5..228b02c0a9 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6bb04b4dc4dfb43adb8ae45670f20232e8a3ba50c409c50a86feb96dfb00d56 -size 24486 +oid sha256:000c36468af4793e4956a4a2a2be1867afefa13140219e2dd8b17b1f7670b6de +size 6299 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_7_en.png index 39750c36e6..3473f077b5 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5694d2ee8d939aa3cc5107b7601ff251481fc8226981208b603507d407dcb12a -size 10637 +oid sha256:a6bb04b4dc4dfb43adb8ae45670f20232e8a3ba50c409c50a86feb96dfb00d56 +size 24486 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_8_en.png index c01e5b8a0a..39750c36e6 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4b01a055e3e8f64b18d045717ee2d6c9795ef1cf2a923c0fe3c5ac26f6e9a62 -size 17091 +oid sha256:5694d2ee8d939aa3cc5107b7601ff251481fc8226981208b603507d407dcb12a +size 10637 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_9_en.png new file mode 100644 index 0000000000..c01e5b8a0a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl.members_RoomMemberListView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4b01a055e3e8f64b18d045717ee2d6c9795ef1cf2a923c0fe3c5ac26f6e9a62 +size 17091 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png new file mode 100644 index 0000000000..85f39a65e2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e3400d75df1c13bde15394e1ab69df7657257bce86b7ac16953142ea7c61389 +size 41775 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png new file mode 100644 index 0000000000..fbfb4d1992 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ba55e0d3271f81d8bcbd6f0a956f50c8e4ed082b23f565357c0c732f2bd7395 +size 38805 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png new file mode 100644 index 0000000000..f15bd024a4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:659e2f942ff1ba3aee9e63ec329aac2386e755c04324c3865dae892f67f5ade5 +size 38762 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png index 479c21d680..726667ed5b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7479bd11493b82e7027599a938979fd50fb75c31cf7c505cf8669db9387fb124 -size 38805 +oid sha256:f1a69af9c184578a355345037ec34016c4b4ab26a2719bfaffafa1027365a678 +size 38453 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png index e3715da6da..62f03c88e1 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetailsDark_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56418af183e3287631cead30c1077d5734ff59f014c03e2ea0df8d91d38ade01 -size 42255 +oid sha256:c41fdaa2341788494026277683590e0fe58fcc6b4c93919c77dcf29fd1e95c15 +size 41752 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png new file mode 100644 index 0000000000..b64c6af3d4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:398dd93176f5bb6058ccd8a3d4257bec7d14ebd2043d11c6b9f0de0108bf9b68 +size 42708 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png new file mode 100644 index 0000000000..1d721d5c87 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a67e601d851d95f73fe7f6165b91703e87f6c1f71fc6c1d9c614d46ee35623ee +size 39515 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png new file mode 100644 index 0000000000..552e6b96c8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_18_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:533db6c347770157ef484b53218885a04f12f732775a94da8b915d700d356256 +size 39399 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png index 8953624294..a51874b64b 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56c21416493a55bb9f105a9908f2b9e07441ec5f5ae3afb68f59fb8329cdb242 -size 39527 +oid sha256:e99e06f6281be006fb12e0a56e7523c8144256562464474cb2aa918f6576d636 +size 39091 diff --git a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png index 7396ea70f9..06d2ff4d32 100644 --- a/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.roomdetails.impl_RoomDetails_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:128bc1d07c659dfdb25a5cac2513ef3dfcad6a156fd3a38457eec40a735e74c9 -size 43265 +oid sha256:d59deaee5d608c279670104c0f3bfcb5bbda90ac9779111441053ea1b9d40d4c +size 42658 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png index 524da3cf89..004d431a95 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a59b21a075f8fe8c3752c92c91741c19024555c5a691b4449e82113f43abb33a -size 31009 +oid sha256:c951ff5fbba4f82d02e3b5c34771d188777d1db316756e7bac96b2dbf028ee52 +size 30931 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png index 461df5677c..e2be7b186f 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdbbc2b01bd3101a37d9a03d3004c5da061302bffb6daf9d8163440ad039f64f -size 41817 +oid sha256:94a8c329900ba25352404abfa6e485c876f229a33abb4771c0632ef2dc18637b +size 41786 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png index 1ab3444ddc..0ab79fb684 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa9fb2c97f38fc051ffdfe16d1a396a1ed124505718eea4129c37287146ab764 -size 41994 +oid sha256:a4730e96fc6edc15712a254bd9fec7d3a0b7f1da0e16304a951a8d62cea96398 +size 41962 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png index e15894514a..2ad2d40892 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9519aa6a3f40251e0731fd493f5b5da576d6f5c728df1e9467810ecc252b8af8 -size 34941 +oid sha256:6f4eacfb8d56d34ae0d85734c6596e86d3ed3b9a664104ac82808c721af9e666 +size 34869 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png index 152fed673d..6d0b4ff7d5 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00c8ff5a2adebc3c09c675eb1d98844a4f6f6825c2a4b8b7dda44349d531d063 -size 30171 +oid sha256:03f17ed51483a5033320e4e4855a3161c208b4a85effce4fd5ad4facaf99089c +size 30099 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png index 03a3e243ee..887f6fd662 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dd9e4cff5ac7956d5d252f05c7705192ebb2c5b7a3a51b8f6f7e93cf3b2b6ea -size 40678 +oid sha256:2e4d51d755743a55f653ba7534f1794b13752f391bb47866106ec0b48e13f060 +size 40611 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png index da98ed73af..960223ada1 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9df69deb26c63a8adb3f974c09694c2211558e504801b7a1050ba06a37a56758 -size 40552 +oid sha256:4899bbf943966cacb39756ec7036579286dff12bcb9aa5b573e09a0929f5294d +size 40517 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png index 788e7eab99..73f5f41673 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90508bcb26e46c02968956c1e5dd28e9cd36f29c165ba9855df36602482335c9 -size 32544 +oid sha256:c836b91ac01dc49cd0d46137b28183d5bdb8cd3830728bb3e5e0e9af3d6a3420 +size 32473 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png index c68f98b47d..5b439e89c9 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9416ca672e6c8c7717cc7605163ac3770b54341acbe93a8d78b26a6a73d73fef -size 69049 +oid sha256:d0a50e8f002b7866579ad058d1d71621e85c00282cca4b5ddb1ecd5d3fb0bfcd +size 57245 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png index 8514956582..c477c9e6fa 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edec074dae0baeaeaafe0e64a8dcfcca24a1151faa9667dbedba97a022c277ae -size 58627 +oid sha256:91759024a0698ea4d4fd05fada6125fae5f86978d26e86d3cb088f57424772cc +size 49470 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png index 57411bfcbb..6bc5c9c9b4 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05d35e9dead958735e5bb8974560bd688258491074eed2262a9aec22669efaed -size 68014 +oid sha256:db0eb360bed04c1169be840fa15429a180b9812b0f38b903639de70cc0eb6369 +size 56370 diff --git a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png index 5e7b562a93..9557e5e9f4 100644 --- a/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebe663ca2ead6eb6a196ceeb99eadc9434eef40aeb2718b4ec6e5945c4ea08e5 -size 56768 +oid sha256:11213aea7ceb0eab52a27ecff95075e3b15508bcfeca04bad88326cbb3989f9f +size 47362 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en.png new file mode 100644 index 0000000000..b467353a6e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d23d7fef77a749e30f90e2222d7c0c5dc191aca7c3b94ebf05ed449dfe7c4b5b +size 24276 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en.png new file mode 100644 index 0000000000..674d4f8fb2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b5727b159ca6d4acc82c72befac497a6179b60696184e53f60fc0ec1cd6799b +size 24107 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png index 1296c86341..7653b90703 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f41090fceca5845f88dbbc6b983e6330ecd3b86d2056ba4584f1fca26305bb0a -size 28943 +oid sha256:4e395d9ca4fc6b2eb1ea9a8fd6e2ad34e7397d9f64cf39e05eed50a1bfffabd7 +size 24186 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png index bceae72d32..9f82a024d9 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0516c0355081b7b2d745ec46fd160fd566be224ef7d350849c9990a8c6320563 -size 27018 +oid sha256:87cb4255c8346796a226cdfc2afb67641a3b27c2c1a15629a07c3293fcad5131 +size 22078 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_3_en.png index 630ea68a0b..c07abc7b58 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f20c32a85bf93f25c51c18b79adc0f8237173dc9fe0705570634adbcc94b1350 -size 36484 +oid sha256:ba80787576dd893f90fcdc83021e47abbde4ae2bcaaca2c167178ba7c2be3886 +size 36481 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_4_en.png index 623baa9c2f..c85a2d454c 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cc7bdca7fe74fe0b16ab23eb4866b3af5245ed762c74a1edba087070cfe32b8 -size 30448 +oid sha256:3a2278fd516dff3a5c86702f09d5621e8b3bb3de1df702b5036fa114975e4d85 +size 27844 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png index ab96c44623..ca9fcec377 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfc7c5d99c0d1e5e0194a5961aac43efea94ae100fca0249f6b00e20b47cbb09 -size 26565 +oid sha256:a465e6254ae595d62a335914414ad2f33b76736f529d1e188f3d0e0d57268f65 +size 23015 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png index 7ed764b198..3c27d5ab57 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2f9d02116554413601c8b11fb75581f30ebb6ce5b8ade2a5a62a0aa4ce04705 -size 29914 +oid sha256:ef88b209eb93fb6b4e5aa411c4cdcb9ff1aa49bd44f11c2271a4d758482b145a +size 25181 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_9_en.png new file mode 100644 index 0000000000..cfea88a8e2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ab40b06cc3b9e9b1926085160d64919dad9be61dcf504ee8778357db952fca0 +size 33062 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png index 96061cad5e..572f9e52e5 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f86dd19d8056f16ae1a5337c49f35d420f2b83baafb29360c8c07c3347d5a93f -size 28074 +oid sha256:6894ddb5662e7da82e5ee42fb0c9eb30afd87e15e415ba42be218ebab434d53a +size 23531 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png index 50419932b1..1acafc94c6 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46d3c77fff43081041fb952fe9c8ea733c5ec164ca9aca548924d3cbefb52369 -size 26131 +oid sha256:0f393f620cc11b7aec7bf52f7efc5a5872ed07a14e7fbf2149b16171dbb2b03e +size 21463 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_3_en.png index db4d01fbfb..4d0032fc29 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7fdbd16b4e78f890f60668fccfbe7af6c6a4f5ba22af26e9f3735dafb13e9d8f -size 33700 +oid sha256:ce136a49edadeaadc229ec485b27bf2d4ae20601e948bca46c632a4035b90130 +size 33697 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_4_en.png index 28ea8e7526..22aeb8dc7a 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f2c2bb7772dd91b37351e4d1e658155f341d83cb6d496eded65b0b543fa2b1 -size 27898 +oid sha256:580fc1fe77975e83cf83fe582c2f3f4aa7003cf302e7dadcd6bb97c04cbbf148 +size 25517 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png index 2e9d6040cb..4351039557 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4acb962472308c94998b3bc67d71a6a69b9bad0294461df374f7e74dcf703328 -size 24379 +oid sha256:4b48c3e2c5e06a612473e252ba52eead6efd741b7ea21fdb778ec9ed5952a2a2 +size 21181 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png index 14bbcdb78f..2c7ce358ee 100644 --- a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a409716052c9c12c963668098943b8f286e41d6a10f5639d11b7b464cb9d0c3d -size 28909 +oid sha256:201c3f2385059321f7500ca8d559bb973a51aeb56fd417e943be496ae55d0869 +size 24370 diff --git a/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_9_en.png new file mode 100644 index 0000000000..912e7ac5bf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.userprofile.shared_UserProfileView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f34cf73456894888268d0ee3d75881a11a3efb4c09534d6885f832e20081f57 +size 31830 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 new file mode 100644 index 0000000000..53b828b23e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8634747fdeca8b754396dd32e02c36956017860118dbc33098aaa10fee358ec +size 28876 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en.png new file mode 100644 index 0000000000..88d6b6c435 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66af8c7679f62fede7b09d6661f0b78d7264a2a415cdf5faf0928aa115adfc2b +size 24514 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en.png new file mode 100644 index 0000000000..03a91ddf87 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c89798843bd3bc2a02094e200ddfc4fcdad8e71554d64c3f0cf2bfc75e894dc +size 24514 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en.png new file mode 100644 index 0000000000..03a91ddf87 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c89798843bd3bc2a02094e200ddfc4fcdad8e71554d64c3f0cf2bfc75e894dc +size 24514 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 47148f4301..6e3fdb2bd3 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:2473606524b856b4afd81e4a32b29c7456788746078387fff280f3c8eb577396 -size 41622 +oid sha256:1da0edef645c5abc391f0425e3a1f8719c1f061f66517b131a81cdd59d2bd04a +size 40105 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 c12e74814b..6cd8aaacec 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:6a8c359687b3ec5f038602b9022ef3967af12c6632a59c56bd10fecf7d92233c -size 46457 +oid sha256:8ebb02ae3454f3cbb28e3aac886d088888251d25ba2c5e8fb7adde700f10383e +size 36323 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png index fbae9acd3c..d7df699feb 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6576c0b77984df1930f0115e1ea90e6720f0fb5f500e186c591f0fd2eb771b6 -size 44729 +oid sha256:682472baf2a1bc5f99cfadc3177615167f1f7531923ec34374f59e263b50d79f +size 37049 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 683805307f..7c87a4d514 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:90c8e131e639b6638a75468d41a3ff0107822e3450c6edbcc4d9eccdd180f6ae -size 31537 +oid sha256:f8aa70b0d5eace900fc8c58674d2f3cdcf409335a33e8cf3eea5f60fb495d009 +size 32299 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 28a80f269d..3bd25e8667 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:3fc2ecdf5b8880b67e9ee9eacba9636819396236325f6ce28f0bf5755fa5ae5e -size 21892 +oid sha256:89bbdbeef7d2d331e95907a4c1b9d34764c3ea39b8bb63a4f42fb210515de75b +size 46713 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png index 376d2f7449..115ae57f89 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8371da870ec795fbced08edd199c6e6c53b1557b648686fe89c278def41e425b -size 24134 +oid sha256:a71a88e65140d150cbe50d116e13119dcb9bd96191832ac482f423f284c55246 +size 47048 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 376d2f7449..7bd143c02c 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:8371da870ec795fbced08edd199c6e6c53b1557b648686fe89c278def41e425b -size 24134 +oid sha256:531de4a36c2fd9b52e7f29fcde221bd8897178bbfb7cbf5b5640acbee4439d1a +size 40396 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en.png new file mode 100644 index 0000000000..bfbc350682 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74b41a474f1c26e668b05c9b9da5313739fc23e2679d7f58d17326499fd8a8f3 +size 40733 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en.png new file mode 100644 index 0000000000..32d51c2a7c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cc1a6ed77d4bdca2154e6a98294a970071f48bbd986bfb175a5ed0f0360d3a2 +size 31534 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 new file mode 100644 index 0000000000..8a110274ca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:227c4049433b734d591eeea0cb5cca65ce27b3f4fe385eb224ec322148865f6b +size 28321 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en.png new file mode 100644 index 0000000000..f86af6507d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0393e0549647a0fe6ebef2372d9e6885db7225d137e44d4057e3fe0a15b1aa8 +size 23895 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en.png new file mode 100644 index 0000000000..8ecf2388f5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb51fe5a51969f768609a5e5eaf5baa95005b094bf1995c3b304f637bf58fbc7 +size 24251 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en.png new file mode 100644 index 0000000000..8ecf2388f5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb51fe5a51969f768609a5e5eaf5baa95005b094bf1995c3b304f637bf58fbc7 +size 24251 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 d9bfdf1991..848a72bbd5 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:6948418922ae7fc3fe1ec3475635cb8c53b15fa49128897e24b39f9b2dfc02f0 -size 40413 +oid sha256:4087f776842690767225ab62c5c1d7717cf039f4ec53494f834cbed9d1cc21c5 +size 38971 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 924aa9fc28..8d074c872f 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:f385489dab05d85ad04be5324e2b6d0ca4333f46671aee69ac11a9feb04f966b -size 45526 +oid sha256:31d4e4cbf57e53a5bf5e63daf0b1926d39dc67d2859a436fe1eb548830897ee3 +size 35532 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png index fc0ad477da..142cf5e2fc 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7792bff8e012d42e7dea995152f5c0674f1c4a24e333cbfcb52d4ad4fc58ec2 -size 43783 +oid sha256:67f80eb2f896018c000419a2d57f3eb198951dafb60fa5e6e57dadd70170e711 +size 36091 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 c34fdec657..f2049210ad 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:744f66b5f395a854dce1f9abc8dbb43fbbee5de1d18b6c18d82a614eb2d32096 -size 30762 +oid sha256:6b753111cd6ca0f030222d97ebbbeb5deb9f3b9d64d82dd8970116a3913be06f +size 31615 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 e359daa223..85af305006 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:a3a1ef5cbd6ec289fe3b9a48811b77b37f71e53fa2be4e34d16a27fae8189fba -size 21226 +oid sha256:fc7264ee36a36ac79f9c52ab24f2cdccecf149aee3b36f1f03121b5a98ac7aa8 +size 45556 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png index a585374439..ff0e6ef380 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23dff654bad158f522929fdb6fb6911f4c511eb688bb28995f2d2a56bd747758 -size 23930 +oid sha256:adbb4b7a7297bf7e586d663dc665cedc2f7dba0e841a3f93d09d3be27e3527fd +size 45919 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 a585374439..c98ce28904 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:23dff654bad158f522929fdb6fb6911f4c511eb688bb28995f2d2a56bd747758 -size 23930 +oid sha256:d1f6072fb62c959e9685f915c3985c58b9bcc2c630d6bc9a4370f4b2d88a374c +size 39645 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en.png new file mode 100644 index 0000000000..6e06243a24 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82460b85ca35f30990ab76a78e635e6783f8ce39524e3f9fd6ffe5b7caab0a14 +size 40012 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en.png new file mode 100644 index 0000000000..5326db7e73 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f92d9cf0b0ea928325a8e64ffe32bd0484f9cb4130cd9815a2ea6997e2d5852c +size 30675 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png index 7e53aff661..66b44846eb 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1504a5137b90bf6c03ae8d3a90946ae1a9b528013f9c3898a0e91e4d073d159 -size 34347 +oid sha256:bf4643bb5ac46e4e13725c6f333b23a9c5a8d3c54d8f2eecfae9811b7237d87c +size 31303 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png index 6d93a90b6c..7f95a4dd26 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0aba8429b1c853012d17fab62350edab5c91721560978b9024ed436d99f7ebe -size 33694 +oid sha256:c6f9f5dceb1e65cc382ef9a2f52f771635d390779f45304b7a7caeeb9e30d51c +size 27879 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png index 1b3302c89c..6e507eb825 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:615107c5e6d654779b1a8fb0ac3e5511f03ac0eac2a5c3ae0424972b92401ca2 -size 5244 +oid sha256:bb1651647db82174f84e0e166f234e9e397df90fff77dd5f254fe1a18ed23d4e +size 25226 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png index 55f09c6d9d..3212a9557b 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54904239d5e4789b82299ec5fe2d68386a7a77aea9045bde2956c648e68bcb8a -size 30965 +oid sha256:87343c744ceb706cc5ab7d1391f914b0020a21e5e12cb3d8d6af536c1f0f71f4 +size 4017 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png index cd629687fb..98a0ff76e4 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67f61b340f5ebfbec1147f7ef37f31054cd37b32abf88058bdb18970bbf7d3fa -size 30082 +oid sha256:ebc36a702cc711e490f33912735a90f5cd85b1702c352931cd2f91d6598252bb +size 33166 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png index c12e74814b..bf8a3e3927 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a8c359687b3ec5f038602b9022ef3967af12c6632a59c56bd10fecf7d92233c -size 46457 +oid sha256:1d9f27a4db5f7ddc46d1564b16c7efba385ba1c4015473639b34d65875e7f37d +size 23186 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png index fbae9acd3c..16a8ed358f 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6576c0b77984df1930f0115e1ea90e6720f0fb5f500e186c591f0fd2eb771b6 -size 44729 +oid sha256:266f19ba9cc406a67db3ce12b7cdac15452ac8f0945ceccf8e7b9b247a50d227 +size 20367 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png index af703a33cf..3bd25e8667 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bed9db1d0c920e48dd564d76494e09876c948b2fbdadccb8c4d23b49d28d3d9 -size 24255 +oid sha256:89bbdbeef7d2d331e95907a4c1b9d34764c3ea39b8bb63a4f42fb210515de75b +size 46713 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png index 9afd661315..115ae57f89 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5b1f18748888c5f20d663f3c6f21fe577915f0b55e7e963be3ccf7e88cda07a -size 19250 +oid sha256:a71a88e65140d150cbe50d116e13119dcb9bd96191832ac482f423f284c55246 +size 47048 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png index 683805307f..7bd143c02c 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90c8e131e639b6638a75468d41a3ff0107822e3450c6edbcc4d9eccdd180f6ae -size 31537 +oid sha256:531de4a36c2fd9b52e7f29fcde221bd8897178bbfb7cbf5b5640acbee4439d1a +size 40396 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png index 7e53aff661..8cb2aed49e 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1504a5137b90bf6c03ae8d3a90946ae1a9b528013f9c3898a0e91e4d073d159 -size 34347 +oid sha256:c7adcf41fe721cf1bdff61850881590bdcb32cff258343c24eb4e44df7dab734 +size 24621 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png index 2dd4cdac47..3a60005483 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:470cb7c851af9d91cc44d52d502b50764136b079b600ca58ae2512e7ff12e34b -size 29534 +oid sha256:460e39d03c679b6575f37df00a7616ca37f4936c951018fc4bde4a6687175e45 +size 19185 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png index 878c05c7f4..32d51c2a7c 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b07e8c136c56d3e23fe3180e791b414c249865e0d7fa5132393bbf7aa09a72bf -size 26631 +oid sha256:9cc1a6ed77d4bdca2154e6a98294a970071f48bbd986bfb175a5ed0f0360d3a2 +size 31534 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png index 7a11690369..a01d276dd7 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14770fdc3e68b250b5f7dad6bb6fb1bf387c66774287276ab4b505c144007b33 -size 33299 +oid sha256:4fe86ca7a863bb66a6abe6febcc1ee4eec188d57a18f76dd973f3dad00911691 +size 30570 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png index 71b604907c..7674d03002 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c428e8289390624b3e4f606404ac54acc19cbe0a6df666661f309b7cc0a88031 -size 32082 +oid sha256:68fffe7c273430425bcf00ed18174c47023e114d9df8108f0681c61cb88ee6fc +size 27229 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png index cba2bf39b2..4e0f525fec 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a360e21538876df4d8aa1b4a3e95e4982df6307a69df4d887416cbbd76b8cd99 -size 5250 +oid sha256:fd887339bef515c0403052338579d5885d6f0f2c31aa3ce511521a97841f3dc4 +size 24531 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png index 7360143942..850faebafa 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_13_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1011caa6960fb64acf48f5d6c2933c9826f66afcdb80280d8509123fb507ab53 -size 30283 +oid sha256:c6ec71c6d10cfffbffb89364967f721762040da16b129da701eccb5568aa414e +size 3999 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png index 43a7fe5af4..122bfaee2f 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce120b819be555167e5c6200e068bca8c1f3a1245b5a6c7939fb012c793a4a57 -size 29220 +oid sha256:3129b8b649e5fb26cf559cce754a84788c186acee1e201989a3f405a8a4c71ac +size 32354 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png index 924aa9fc28..5e7f4fa0b3 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f385489dab05d85ad04be5324e2b6d0ca4333f46671aee69ac11a9feb04f966b -size 45526 +oid sha256:90a6b06c59eb121a265f33299a97360e3a81c29daafc850c65f658cd4ffde99a +size 22695 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png index fc0ad477da..a839708111 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7792bff8e012d42e7dea995152f5c0674f1c4a24e333cbfcb52d4ad4fc58ec2 -size 43783 +oid sha256:84f577862d06ccb4f5a4c3ae9d95574dfc884f52ba08e121708c6833b417d130 +size 20033 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png index 5883152c3e..85af305006 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2542e7ad670ba57f203c3c07d9f6dcd654100fd7c7681846fdf3657436f261f4 -size 23951 +oid sha256:fc7264ee36a36ac79f9c52ab24f2cdccecf149aee3b36f1f03121b5a98ac7aa8 +size 45556 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png index f3ea2ba2cc..ff0e6ef380 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41fa05234a46873d525a7414191e110e157d8226dc5e034bf9ddbbbbd33cde37 -size 18696 +oid sha256:adbb4b7a7297bf7e586d663dc665cedc2f7dba0e841a3f93d09d3be27e3527fd +size 45919 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png index c34fdec657..c98ce28904 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:744f66b5f395a854dce1f9abc8dbb43fbbee5de1d18b6c18d82a614eb2d32096 -size 30762 +oid sha256:d1f6072fb62c959e9685f915c3985c58b9bcc2c630d6bc9a4370f4b2d88a374c +size 39645 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png index 7a11690369..25f198a9e5 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14770fdc3e68b250b5f7dad6bb6fb1bf387c66774287276ab4b505c144007b33 -size 33299 +oid sha256:b41f5b0620537d0e31e5bad7a6e763fc3efeb0cd75bf64ef3d283b14b5d5ba49 +size 24271 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png index c6692886f4..656663db73 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9dba1ef09211361216ee035c64ee50d62ab1df5191d365ef26f5a4f741477867 -size 28635 +oid sha256:de7e6aa8b417026b6a344955b478d823cc0118b450020d1cb81b6f0cbeaa1c4e +size 18606 diff --git a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png index 4c3a6af338..5326db7e73 100644 --- a/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.outgoing_VerifySelfSessionView_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0601e1c9d553113eb16d89e888e23320585ea139a6653d2b56e830834ae4fe68 -size 26011 +oid sha256:f92d9cf0b0ea928325a8e64ffe32bd0484f9cb4130cd9815a2ea6997e2d5852c +size 30675 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 new file mode 100644 index 0000000000..38e1253d41 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5d78f00d33cad5e00719eec49f6b55959b5c23b3842a0f2abc9280961ebb29d +size 12660 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 new file mode 100644 index 0000000000..486dae34af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4612c8404b671a01129d2334a7aa296218bb4b00820265ef5d00fd9603e7f850 +size 12272 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en.png new file mode 100644 index 0000000000..20de46a882 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bedf319e44da65199c96bb1a503d3a4c961e91eb5132553b50aa13cacfc5ceaf +size 12519 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en.png new file mode 100644 index 0000000000..2cb5df9203 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4ca72f4c73d4d9baca7cf8fbf6a7a0984bb9681d5fc252c7797634aeef05998 +size 12308 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en.png index f3637fd6ad..20de46a882 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f21517e6cefc67709164b03b6bf64765702532d9de4a4ca837b3b586a4125b10 -size 12458 +oid sha256:bedf319e44da65199c96bb1a503d3a4c961e91eb5132553b50aa13cacfc5ceaf +size 12519 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en.png index 24f77666b0..2cb5df9203 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c32ca21a6b99ea3b3aa05713247b2338af98f6830180dfab6d18902baa23d4ea -size 12274 +oid sha256:e4ca72f4c73d4d9baca7cf8fbf6a7a0984bb9681d5fc252c7797634aeef05998 +size 12308 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 new file mode 100644 index 0000000000..79872e5244 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_90_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3667c472d9832e0d25996889ec0887685dfac049c043a6c8a448789a5f7c6909 +size 17200 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 new file mode 100644 index 0000000000..b630ea26fc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_91_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f96f824d47ed90dd4fee4cd64b807bc49dd273510d6cd2072b2bf5cdaf740dd +size 15943 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 new file mode 100644 index 0000000000..a4fd665ea1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.avatar_Avatar_Avatars_92_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42488ef04ffbbe625b7da963a2b1c3c9cabb0d80bb77a2d4f8b4c4afdcd9bf95 +size 20337 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en.png index cb09d01ce9..afc6b91e2c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e51f7c0e57fbdbeea27962cf3ebf58ead3a5689251eced57f2a37cb4373c9858 -size 7057 +oid sha256:867a27b40625fe6d0c214c3a4bea0a09baf298aebb53d3ff77e19b3dfe250414 +size 10393 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en.png index bef7df4b6f..d291068fbe 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd6fd55c333b87c619189f4b0e1b1b0616f65982b322e03e271a59eaaf1a649b -size 6967 +oid sha256:df3a081fb057accffb4179c86f46cb05b033962c0339a96767b68610b9df9ecc +size 10313 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en.png index 7a6b59b289..be39302a17 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6eb030d6d2f1c80cb7dea618c3a47db2f7db7b26ad19b3ba9313c0535ab7a761 -size 6533 +oid sha256:c43a92d9ca48521da56819a405585a96255b302a517832b7375870752489dacb +size 10516 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en.png index 0b7e398df0..7e813bdae9 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b955337a438e1c29cd75961dadc489d14f726f7524281f4368fac1c1cfcb58d6 -size 6512 +oid sha256:82e2d283d97af1db75bacef222976be6e4f646d5606e9f4aa95fa15b81bb0898 +size 10414 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Day_0_en.png index af2614df6b..9635585479 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ec29adc030f96aae3952c785b71b2e712d9f4a6cc6cced5f4ac139f5ba4f3a7 -size 12393 +oid sha256:1fed22f221c1087ffb066b950918924cfc4af1618bb7724c57fa27d4cfe50340 +size 14089 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Night_0_en.png index 82c063087b..08a56eb13c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_BigIcon_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4aa2203ef22231bf8368849fcf1b17feb08292e116e5e33ed67c3f29ff138d25 -size 12535 +oid sha256:b028b36b64fecede982569046613903a8df32e7f95243a0eb4c25e89205c141f +size 14199 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Day_6_en.png new file mode 100644 index 0000000000..0684f2bcaf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Day_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d81aade2945be977896038addacdb9246d17bb1a57fb3bd0d43169e382ce9f03 +size 13126 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Night_6_en.png new file mode 100644 index 0000000000..7e7a23ed38 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.components_PageTitleWithIconFull_Night_6_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:610e68016011b14db56217ebcd27a86ab597c1668a3a90b09c3e39ac308f8764 +size 13054 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Day_0_en.png index ca94a884e3..65bc8d2f20 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:560c9159e78e9da940da58a4297aca1ac647218fab0e03c532016ac96a3a560d -size 21524 +oid sha256:b5e45da502ee63629d7a0ca130ce92bdc33e2c93baf49f50594fc8831771c305 +size 13760 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Night_0_en.png index 6793e1a405..13c6908f5a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.icons_IconsOther_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0bb37fb7f6dbce206288431c59e3cc1b31d1a5a25d73b2f2321d7d46a459da5 -size 20631 +oid sha256:4fb44869e4f1d3233d316e814b5d88a94177b8bdae9eb19186ae937641570ec2 +size 13355 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Day_0_en.png new file mode 100644 index 0000000000..f81bdb1027 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6489b0d11a7e9d29836d09e9f5507850d1a978c6810ec19861823a17c6c9d94 +size 12550 diff --git a/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Night_0_en.png new file mode 100644 index 0000000000..7858b1f1b9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.designsystem.theme.components_IconColorButton_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:673097fbe509f075a9c7e48f72766e5153cbcfb49643eb1f49636d5d27431e0f +size 12147 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_0_en.png new file mode 100644 index 0000000000..1b3302c89c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:615107c5e6d654779b1a8fb0ac3e5511f03ac0eac2a5c3ae0424972b92401ca2 +size 5244 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_1_en.png new file mode 100644 index 0000000000..1b3302c89c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:615107c5e6d654779b1a8fb0ac3e5511f03ac0eac2a5c3ae0424972b92401ca2 +size 5244 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png new file mode 100644 index 0000000000..cfe67410f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:783bfa52d591b0ae98ac55cf5da8c4a7d7a28275e2714ec7278d4a7947009f4d +size 6187 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_3_en.png new file mode 100644 index 0000000000..a788378045 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3e94d893ac1bc9fc13806556332f6639469abc620a7617a730a8bbc9153f826 +size 7070 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_0_en.png new file mode 100644 index 0000000000..cba2bf39b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a360e21538876df4d8aa1b4a3e95e4982df6307a69df4d887416cbbd76b8cd99 +size 5250 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_1_en.png new file mode 100644 index 0000000000..cba2bf39b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a360e21538876df4d8aa1b4a3e95e4982df6307a69df4d887416cbbd76b8cd99 +size 5250 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png new file mode 100644 index 0000000000..cf3bd3a0ea --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e374bcd5e119bf944eeaf28876b7e902c1bbd2e6bbf84e0f30ace9732675b9ae +size 6142 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_3_en.png new file mode 100644 index 0000000000..47cb88a813 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bd12380891a5af74a827ce14393bb5f79d9019eec7653e10ecc2923cec3c2a9 +size 6945 diff --git a/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_16_en.png b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_16_en.png new file mode 100644 index 0000000000..504244640a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/libraries.mediaviewer.impl.viewer_MediaViewerView_16_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dec2e6f9dcfc2e92fef730a26599d6a4e2e09b6b9999dc912a82917f65908417 +size 6798 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Day_0_en.png deleted file mode 100644 index 60d16dd429..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:66b9db2c2f0af1d0aebffdd74ceb8177aff237bc8f7d84deb13501f8aaed5872 -size 4583 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Night_0_en.png deleted file mode 100644 index e5341ba23c..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_ComposerOptionsButton_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c89e2c023db9d402fe28ed322173938df1c6b5d6dff18a4ffddfe768b9b992d7 -size 4587 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Day_0_en.png deleted file mode 100644 index e42669d452..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Day_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4dd9d99e40d9991117971f912f45cfe0457917b77de9e90300e80768d8a2037b -size 4840 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Night_0_en.png deleted file mode 100644 index 7a6a922b78..0000000000 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer.components_DismissTextFormattingButton_Night_0_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ab53f98e76b04d78497fc1b77607eb65b6141a98d7895ac0e428abede8b148f -size 4814 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png index 63b8304a5c..31601587e4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37b6772ad2ccdd8f3f7536a4dbd036223380d98964cfca299ad04d06f46a33d8 -size 52481 +oid sha256:e64de19f68dd0135905925645c646890d177a2648b68ba4c3edf1769725cfabe +size 52614 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png index 8a1d75158b..596eacb886 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f354e23e0869ea9f0b9d9f04a3ed70a7c4220c4f3bf65aace7ea367cec52167 -size 50905 +oid sha256:32fbd5db3fe38e87e698a03347b1ee6036520ef892603d158f506c45f43e0fd2 +size 51053 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png index 63b8304a5c..31601587e4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37b6772ad2ccdd8f3f7536a4dbd036223380d98964cfca299ad04d06f46a33d8 -size 52481 +oid sha256:e64de19f68dd0135905925645c646890d177a2648b68ba4c3edf1769725cfabe +size 52614 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png index 8a1d75158b..596eacb886 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerEdit_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f354e23e0869ea9f0b9d9f04a3ed70a7c4220c4f3bf65aace7ea367cec52167 -size 50905 +oid sha256:32fbd5db3fe38e87e698a03347b1ee6036520ef892603d158f506c45f43e0fd2 +size 51053 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png index 75bce92af2..924511496f 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea9e3504ed08cad61dc583f760b38743457770ada95b4cec544bc4b6804e062f -size 53519 +oid sha256:59ff13af2bdf76abcbc6502f9a6159871cc60ddd3d693ff50bb1ed3f3298dc1a +size 53921 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png index 1a6eaebb4a..73d7d057bd 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerFormatting_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b83cf8c99cdda65aadb0a4ce8d6521945b3d87e304cfbf0f10f63e360391e3f -size 50978 +oid sha256:2f4896534efc9e1d1dd91e307609aaffafdc643a81ffca25ed9589ea424b2e60 +size 51303 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png index 26cdb69f7b..016ad32f43 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a315b0a33c2034828d8943f64ad88f3a2e3b4a39db17e4885a80d387be0e1a06 -size 76291 +oid sha256:c45c431a00a4222d48d7ed9bea28d4b87c744c543ac34345418897f338d45bc0 +size 76396 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png index f3152152bd..aa7945b5d4 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49c600c54b3ba8eb98af152a9867f7a1d8740c76f51f4b0d85af95fb5f716e34 -size 59323 +oid sha256:0b622bd71902638a554124a38477e7ef7fecb5a8a9a4c74fe22c291ea1b38a12 +size 59392 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png index 504e2bfb35..dda693a4d3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:807de7ee3bd5c8b1fceca0b9149c316ea84ea9bb8415b7b0b72840204e770af2 -size 74336 +oid sha256:f7804f57fc5951d12fcf260dc9469f4ecf30ea7846feb75386fc0d45a577ab61 +size 74433 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png index 948e536722..4015d949a9 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1783e2299851c1a82a57b5434cc5488c94967622f886c267fe5346c42f9c52cf -size 85709 +oid sha256:74d0d5797e35f8bb8d07c58826dd5c6dc485d179ffef0d32d356a0dbd0e0af2f +size 85811 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png index 288474acc6..cac2f14f59 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8b5f3892fa9bddde1217f328ba39b9aa99080c2dfd68e50221361e9529c0dfa -size 62559 +oid sha256:7a5f9dd27972e5b5598dabb207f62bd36a6fa8c618cf9f764565b2ed8f694280 +size 62643 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png index 820d5adb77..59378d6b5b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd1e1126ac54b07692153a928e3e4b1c555115738416f510220cef4834b52870 -size 61633 +oid sha256:447046861cf9c48ee6381a6d17d72d5d675b433a39df2d04a98f0d320ab3d972 +size 61729 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png index 6bd49bfd24..f37e3fa38c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:509f8ee34c3d526f3c96578dbe00c9dd63e2178bea8ea7edc8467cd12cf8f622 -size 69346 +oid sha256:4a5a5d575648186710be4b466bbae6b8b7d7064be27042bdb3a575b90189d906 +size 69437 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png index dcf27b2bb2..b1de8872f5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c666853bb32e576090f3d314097dbe7d2daa85bad949ce9c3ad152847c743a0e -size 59726 +oid sha256:c8788be00225f0547c54b9fc87a92497a86c706ae0638f3988e58518f8404ded +size 59817 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png index 3732d0b8f2..df00d4d632 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19b169ca0a32a4aec13012495fc2d83ff53ab2d096e9d7630d2e84317302c67a -size 60548 +oid sha256:c990db5dda7d348b61e5a917575aaacc4b1ca2110fe6bcbff51e0eb3a718a342 +size 60629 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png index 72c517aeb6..db527305a1 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7cf0f8421c9b23a99450da474d3fe99a7453472801acaaecd34868a64060dcc -size 62775 +oid sha256:2cf3f968dec3e9f4bab00e3ccd91ca432dc042dd49151e27e8d5ba07d399ed3c +size 62862 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png index b9461d92c8..8ed46b43ae 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e36d27009e23535f39ea20206af67a95368d0cc674d53e9085b111e48a6beea9 -size 69949 +oid sha256:4b95759b9fe7bf8d8807e35d5fe576d750620d7eafcf3d17a3e3f85532048e98 +size 70037 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png index ccc32f4a1e..6ec3428f1b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Day_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:405f65309e783c2c099f45e6be528c05fbfddebd37accd9352a9208588b6e178 -size 60015 +oid sha256:3400d8a1c228da983fe9af087c38141d5c8f195541051bd250a51ac138700e7a +size 60083 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png index 2685cf557a..7c9aec3c79 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c6f039f8db0f5e95ad240697157dddd2115876deec42616a6b43ddcab6e4d9d8 -size 73593 +oid sha256:ada8a9f61a93ce6ad58098b7e0743b23ba0ee50a6168eeeb34d55584a9796982 +size 73791 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png index 80fa524c9a..9806c1a441 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:15a0cff9d79d66b032a5b8cf99e731299fe56bf8de7328c858745974bf776b8d -size 57060 +oid sha256:95648a88ce6afce734f8230a8fc1b68947a2a0889e259621f68ea376e11eb943 +size 57265 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png index a7d0ebeed5..a01b5a10e3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_11_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3edef0741d8f431ecaa4fcb5e649101916af004f6adf6315f01d657adb90d0f -size 71777 +oid sha256:40245180648f6a71a7410bd7ce29cf5bef3de17afec4a9c4f2673ca9d16fa42f +size 71970 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png index b29d8c5a40..5f876aafbb 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5db1a345670e7ae8af6149e97bf1dc6362326f1de55e4884a95b248591562f3a -size 82831 +oid sha256:bac3e0bdcc1bf5b3fca6af2bbf9aa983bb0fbc412e179bdb18badf6d5d95d4c4 +size 83026 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png index c9992326af..ff8e196b9d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f455cd10db8660d33aa51b6c487d2dd2b86674d94a60a38a83cff3785ec48524 -size 60328 +oid sha256:2a89f4b6cf7529bc75a6ca5e478e0c5e5b0d1d788d4e1ab5a72039d108239a4a +size 60538 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png index 9a0bd6db46..aec8ccba5e 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9cf437b56de3187e70e1e0486a98bc9e18ba8ab8645a6e19c6e042d82f721426 -size 59467 +oid sha256:6c7426edf9d3405664abc4882afc62aa77ca73004c3d167d52761b1d45ccc914 +size 59675 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png index 8da7483264..66b80b5623 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3013792b094d1224e76b9428818083f4d586e759f3de412e6f1139df572b9d0 -size 67069 +oid sha256:d2178a1374d8ca42634347b9ae3ad7ec991e05af98b4503b29f3b2709c6c1bb0 +size 67265 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png index e6a5a6b564..b747e6c95a 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_5_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4744282f54264abc7cb833efb7b178acd6e334bf03905e0a0f5d1c21b427da9 -size 57653 +oid sha256:f13cd481c6610e3f19c279c0ef16c0811d02d9984280988808858219c9433eec +size 57856 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png index 682e6c8ecf..be570c3a64 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_6_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c94c6a421aba335a6af4e6548d0ceba7f09e1dfe34f8813072ddff943eea8aab -size 58312 +oid sha256:3638d634f596cf78d9eda317f2bb7a9646a12cbbf7052e488469094ac49ceeac +size 58504 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png index dab4a85d7d..f20453b248 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_7_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97dd413b09a117bf03f31ea21c62d774618698c44223601667283d17d8af4f08 -size 60632 +oid sha256:d61b1864f43cd9b96adbf2ebf829268d1a7e25e79549bd1698ad55cb4d5fd8e9 +size 60853 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png index 7fba8057e4..a2d603396b 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_8_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea57d3607d3a9f1db5a0f7a111de31859c67c17da09cf0b0fb26485c59e41eff -size 67533 +oid sha256:833827253e40c7600e99cb88f57c2bc8d9125714cc5af476b408a8e13deb5e5a +size 67744 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png index fc9cc280cd..d801114003 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerReply_Night_9_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14082dae20c3a415221fabda507a6d0b338c04f114319fd53bbcf4b1ec4da549 -size 57868 +oid sha256:6d12b040036e4b15256d5ca2458ab784cba9d0914f83b94ad75ff095e11bc7cb +size 58068 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png index 05cb8123c2..40c610ee67 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:663a2f1bada754dd92043b26fdafdcb4974f1dfe8368e4126bf41baa4356e0ac -size 46655 +oid sha256:2ba17f36d661d5b86e43be38b42dd51b4793b37ecbf3e2ca3bdccd3d2b0fef0d +size 46716 diff --git a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png index 6bf091cc3e..834707f0b6 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.textcomposer_TextComposerSimple_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cca813483d17bafd6b2c09759659525b59909d76b90e124e03e7f8e0a161121e -size 44476 +oid sha256:d4f4d67d99ef4403bf479657b5a3823cf33ca3ceee108d55107d0fad7caaba4a +size 44672 diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index 0ebe19bcc4..cd27b0be16 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -229,6 +229,7 @@ Compose: - LocalRoomMemberProfilesCache - LocalMentionSpanTheme - LocalAnalyticsService + - LocalBuildMeta CompositionLocalNaming: active: true ContentEmitterReturningValues: diff --git a/tools/localazy/config.json b/tools/localazy/config.json index f99291da35..2495301fb8 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -233,7 +233,9 @@ "name" : ":features:ftue:impl", "includeRegex" : [ "screen_welcome_.*", - "screen_notification_optin_.*" + "screen_notification_optin_.*", + "screen_identity_.*", + "screen_session_verification_enter_recovery_key" ] }, {