diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf7f262cee..6c77cb3770 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: run: ./gradlew :app:assembleGplayDebug app:assembleFDroidDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload debug APKs if: ${{ matrix.variant == 'debug' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-debug path: | @@ -61,7 +61,7 @@ jobs: app/build/outputs/apk/fdroid/debug/*-universal-debug.apk - name: Upload x86_64 APK for Maestro if: ${{ matrix.variant == 'debug' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-apk-maestro path: | diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index 0d9b5949cc..bce9d923f3 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -61,7 +61,7 @@ jobs: run: ./gradlew :app:assembleGplayDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES - name: Upload debug Enterprise APKs if: ${{ matrix.variant == 'debug' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-enterprise-debug path: | diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 0868b0729f..e4300b3321 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -20,7 +20,7 @@ jobs: - run: | npm install --save-dev @babel/plugin-transform-flow-strip-types - name: Danger - uses: danger/danger-js@bdccecb77e0144055fbaea9224f10cf8b1229b68 # 13.0.4 + uses: danger/danger-js@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5 with: args: "--dangerfile ./tools/danger/dangerfile.js" env: diff --git a/.github/workflows/maestro-local.yml b/.github/workflows/maestro-local.yml index 7481bec0ba..ff0ac49f5c 100644 --- a/.github/workflows/maestro-local.yml +++ b/.github/workflows/maestro-local.yml @@ -44,7 +44,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - name: Upload APK as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-apk-maestro path: | @@ -69,7 +69,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.ref }} - name: Download APK artifact from previous job - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 with: name: elementx-apk-maestro - name: Enable KVM group perms @@ -102,7 +102,7 @@ jobs: script: | .github/workflows/scripts/maestro/maestro-local-with-screen-recording.sh app-gplay-x86_64-debug.apk - name: Upload test results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: test-results path: | diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index 537565743e..080cf99b05 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -42,7 +42,7 @@ jobs: - name: ✅ Upload kover report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: kover-results path: | @@ -74,7 +74,7 @@ jobs: run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES - name: Upload dependency analysis if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: dependency-analysis path: build/reports/dependency-check-report.html diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 5277cbdf05..7600d8965c 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -97,7 +97,7 @@ jobs: run: ./gradlew :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --no-daemon - name: Upload reports if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: konsist-report path: | @@ -174,7 +174,7 @@ jobs: run: ./gradlew :app:lintGplayDebug :app:lintFdroidDebug lintDebug $CI_GRADLE_ARG_PROPERTIES --continue - name: Upload reports if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: linting-report path: | @@ -214,7 +214,7 @@ jobs: run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon - name: Upload reports if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: detekt-report path: | @@ -254,7 +254,7 @@ jobs: run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES - name: Upload reports if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ktlint-report path: | @@ -317,7 +317,7 @@ jobs: # https://github.com/actions/checkout/issues/881 ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} - name: Download reports from previous jobs - uses: actions/download-artifact@v5 + uses: actions/download-artifact@v6 - name: Prepare Danger if: always() run: | @@ -326,7 +326,7 @@ jobs: yarn add danger-plugin-lint-report --dev - name: Danger lint if: always() - uses: danger/danger-js@bdccecb77e0144055fbaea9224f10cf8b1229b68 # 13.0.4 + uses: danger/danger-js@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5 with: args: "--dangerfile ./tools/danger/dangerfile-lint.js" env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2cce85bd5a..29ff4a5373 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,7 +38,7 @@ jobs: ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }} run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload bundle as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-app-gplay-bundle-unsigned path: | @@ -74,7 +74,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload bundle as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-enterprise-app-gplay-bundle-unsigned path: | @@ -102,7 +102,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} run: ./gradlew assembleFdroidRelease $CI_GRADLE_ARG_PROPERTIES - name: Upload apks as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: elementx-app-fdroid-apks-unsigned path: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 22c302cbb3..4965530b5a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -61,7 +61,7 @@ jobs: - name: 🚫 Upload kover failed coverage reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: kover-error-report path: | @@ -73,7 +73,7 @@ jobs: - name: 🚫 Upload test results on error if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: tests-and-screenshot-tests-results path: | diff --git a/CHANGES.md b/CHANGES.md index 6afb930a5b..1ac0cd1124 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +Changes in Element X v25.11.0 +============================= + +Hotfix release. + +Includes https://github.com/element-hq/element-x-android/pull/5615, which fixes an issue that prevented Element Call notifications from being displayed sometimes. + +**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.10.1...v25.11.0 + Changes in Element X v25.10.1 ============================= diff --git a/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt b/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt index e48dd52daf..c9dfd266d9 100644 --- a/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt +++ b/app/src/main/kotlin/io/element/android/x/di/RoomGraph.kt @@ -9,13 +9,14 @@ package io.element.android.x.di import dev.zacsweers.metro.GraphExtension import dev.zacsweers.metro.Provides +import io.element.android.appnav.di.TimelineBindings import io.element.android.libraries.architecture.NodeFactoriesBindings import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.JoinedRoom @GraphExtension(RoomScope::class) -interface RoomGraph : NodeFactoriesBindings { +interface RoomGraph : NodeFactoriesBindings, TimelineBindings { @GraphExtension.Factory interface Factory { fun create( diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index 063e26b9da..9a9ccb2104 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) + testImplementation(projects.features.forward.test) testImplementation(projects.features.networkmonitor.test) testImplementation(projects.features.rageshake.test) testImplementation(projects.services.appnavstate.test) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index 9ae5aa38fe..5e160ed49f 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -21,13 +21,13 @@ import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.SessionGraphFactory import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -56,10 +56,12 @@ class LoggedInAppScopeFlowNode( plugins = plugins ), DependencyInjectionGraphOwner { interface Callback : Plugin { - fun onOpenBugReport() - fun onAddAccount() + fun navigateToBugReport() + fun navigateToAddAccount() } + private val callback: Callback = callback() + @Parcelize object NavTarget : Parcelable @@ -81,12 +83,12 @@ class LoggedInAppScopeFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : LoggedInFlowNode.Callback { - override fun onOpenBugReport() { - plugins().forEach { it.onOpenBugReport() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } - override fun onAddAccount() { - plugins().forEach { it.onAddAccount() } + override fun navigateToAddAccount() { + callback.navigateToAddAccount() } } return createNode(buildContext, listOf(callback)) 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 090ed70fe2..00529bdf20 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -24,7 +24,6 @@ import com.bumble.appyx.core.navigation.NavKey import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel import com.bumble.appyx.core.node.Node 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.BackStack.State.ACTIVE import com.bumble.appyx.navmodel.backstack.BackStack.State.CREATED @@ -67,6 +66,7 @@ import io.element.android.features.userprofile.api.UserProfileEntryPoint import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.architecture.waitForNavTargetAttached @@ -148,10 +148,11 @@ class LoggedInFlowNode( plugins = plugins ) { interface Callback : Plugin { - fun onOpenBugReport() - fun onAddAccount() + fun navigateToBugReport() + fun navigateToAddAccount() } + private val callback: Callback = callback() private val loggedInFlowProcessor = LoggedInEventProcessor( snackbarDispatcher = snackbarDispatcher, roomMembershipObserver = matrixClient.roomMembershipObserver, @@ -282,7 +283,7 @@ class LoggedInFlowNode( data object Ftue : NavTarget @Parcelize - data object RoomDirectorySearch : NavTarget + data object RoomDirectory : NavTarget @Parcelize data class IncomingShare(val intent: Intent) : NavTarget @@ -304,46 +305,47 @@ class LoggedInFlowNode( } NavTarget.Home -> { val callback = object : HomeEntryPoint.Callback { - override fun onRoomClick(roomId: RoomId) { + override fun navigateToRoom(roomId: RoomId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } - override fun onSettingsClick() { + override fun navigateToSettings() { backstack.push(NavTarget.Settings()) } - override fun onStartChatClick() { + override fun navigateToCreateRoom() { backstack.push(NavTarget.CreateRoom) } - override fun onSetUpRecoveryClick() { + override fun navigateToSetUpRecovery() { backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.Root)) } - override fun onSessionConfirmRecoveryKeyClick() { + override fun navigateToEnterRecoveryKey() { backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey)) } - override fun onRoomSettingsClick(roomId: RoomId) { + override fun navigateToRoomSettings(roomId: RoomId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details)) } - override fun onReportBugClick() { - plugins().forEach { it.onOpenBugReport() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } } - homeEntryPoint - .nodeBuilder(this, buildContext) - .callback(callback) - .build() + homeEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } is NavTarget.Room -> { val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback { - override fun onOpenRoom(roomId: RoomId, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, serverNames: List) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames)) } - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { when (data) { is PermalinkData.UserLink -> { // Should not happen (handled by MessagesNode) @@ -369,7 +371,7 @@ class LoggedInFlowNode( } } - override fun onOpenGlobalNotificationSettings() { + override fun navigateToGlobalNotificationSettings() { backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings)) } } @@ -384,76 +386,85 @@ class LoggedInFlowNode( } is NavTarget.UserProfile -> { val callback = object : UserProfileEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId) { + override fun navigateToRoom(roomId: RoomId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } } - userProfileEntryPoint.nodeBuilder(this, buildContext) - .params(UserProfileEntryPoint.Params(userId = navTarget.userId)) - .callback(callback) - .build() + userProfileEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = UserProfileEntryPoint.Params(userId = navTarget.userId), + callback = callback, + ) } is NavTarget.Settings -> { val callback = object : PreferencesEntryPoint.Callback { - override fun onAddAccount() { - plugins().forEach { it.onAddAccount() } + override fun navigateToAddAccount() { + callback.navigateToAddAccount() } - override fun onOpenBugReport() { - plugins().forEach { it.onOpenBugReport() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } - override fun onSecureBackupClick() { + override fun navigateToSecureBackup() { backstack.push(NavTarget.SecureBackup()) } - override fun onOpenRoomNotificationSettings(roomId: RoomId) { + override fun navigateToRoomNotificationSettings(roomId: RoomId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings)) } - override fun navigateTo(roomId: RoomId, eventId: EventId) { + override fun navigateToEvent(roomId: RoomId, eventId: EventId) { backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId))) } } val inputs = PreferencesEntryPoint.Params(navTarget.initialElement) - preferencesEntryPoint.nodeBuilder(this, buildContext) - .params(inputs) - .callback(callback) - .build() + preferencesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = inputs, + callback = callback, + ) } NavTarget.CreateRoom -> { val callback = object : StartChatEntryPoint.Callback { - override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) { + override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) { backstack.replace(NavTarget.Room(roomIdOrAlias = roomIdOrAlias, serverNames = serverNames)) } - override fun onOpenRoomDirectory() { - backstack.push(NavTarget.RoomDirectorySearch) + override fun navigateToRoomDirectory() { + backstack.push(NavTarget.RoomDirectory) } } - startChatEntryPoint - .nodeBuilder(this, buildContext) - .callback(callback) - .build() + startChatEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } is NavTarget.SecureBackup -> { - secureBackupEntryPoint.nodeBuilder(this, buildContext) - .params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement)) - .callback(object : SecureBackupEntryPoint.Callback { + secureBackupEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement), + callback = object : SecureBackupEntryPoint.Callback { override fun onDone() { backstack.pop() } - }) - .build() + }, + ) } NavTarget.Ftue -> { ftueEntryPoint.createNode(this, buildContext) } - NavTarget.RoomDirectorySearch -> { - roomDirectoryEntryPoint.nodeBuilder(this, buildContext) - .callback(object : RoomDirectoryEntryPoint.Callback { - override fun onResultClick(roomDescription: RoomDescription) { + NavTarget.RoomDirectory -> { + roomDirectoryEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = object : RoomDirectoryEntryPoint.Callback { + override fun navigateToRoom(roomDescription: RoomDescription) { backstack.push( NavTarget.Room( roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(), @@ -462,31 +473,35 @@ class LoggedInFlowNode( ) ) } - }) - .build() + }, + ) } is NavTarget.IncomingShare -> { - shareEntryPoint.nodeBuilder(this, buildContext) - .callback(object : ShareEntryPoint.Callback { + shareEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = ShareEntryPoint.Params(intent = navTarget.intent), + callback = object : ShareEntryPoint.Callback { override fun onDone(roomIds: List) { navigateUp() roomIds.singleOrNull()?.let { roomId -> backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias())) } } - }) - .params(ShareEntryPoint.Params(intent = navTarget.intent)) - .build() + }, + ) } is NavTarget.IncomingVerificationRequest -> { - incomingVerificationEntryPoint.nodeBuilder(this, buildContext) - .params(IncomingVerificationEntryPoint.Params(navTarget.data)) - .callback(object : IncomingVerificationEntryPoint.Callback { + incomingVerificationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = IncomingVerificationEntryPoint.Params(navTarget.data), + callback = object : IncomingVerificationEntryPoint.Callback { override fun onDone() { backstack.pop() } - }) - .build() + }, + ) } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index 08bdbadde6..b372155bd3 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted @@ -29,6 +28,7 @@ import io.element.android.features.login.api.LoginParams import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices import io.element.android.libraries.designsystem.utils.ScreenOrientation @@ -55,9 +55,10 @@ class NotLoggedInFlowNode( ) : NodeInputs interface Callback : Plugin { - fun onOpenBugReport() + fun navigateToBugReport() } + private val callback: Callback = callback() private val inputs = inputs() override fun onBuilt() { @@ -78,20 +79,19 @@ class NotLoggedInFlowNode( return when (navTarget) { NavTarget.Root -> { val callback = object : LoginEntryPoint.Callback { - override fun onReportProblem() { - plugins().forEach { it.onOpenBugReport() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } } - loginEntryPoint - .nodeBuilder(this, buildContext) - .params( - LoginEntryPoint.Params( - accountProvider = inputs.loginParams?.accountProvider, - loginHint = inputs.loginParams?.loginHint, - ) - ) - .callback(callback) - .build() + loginEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = LoginEntryPoint.Params( + accountProvider = inputs.loginParams?.accountProvider, + loginHint = inputs.loginParams?.loginHint, + ), + callback = callback, + ) } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt index dc4e169d53..0332f34306 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt @@ -227,11 +227,11 @@ class RootFlowNode( } val inputs = LoggedInAppScopeFlowNode.Inputs(matrixClient) val callback = object : LoggedInAppScopeFlowNode.Callback { - override fun onOpenBugReport() { + override fun navigateToBugReport() { backstack.push(NavTarget.BugReport) } - override fun onAddAccount() { + override fun navigateToAddAccount() { backstack.push(NavTarget.NotLoggedInFlow(null)) } } @@ -239,7 +239,7 @@ class RootFlowNode( } is NavTarget.NotLoggedInFlow -> { val callback = object : NotLoggedInFlowNode.Callback { - override fun onOpenBugReport() { + override fun navigateToBugReport() { backstack.push(NavTarget.BugReport) } } @@ -249,11 +249,13 @@ class RootFlowNode( createNode(buildContext, plugins = listOf(params, callback)) } is NavTarget.SignedOutFlow -> { - signedOutEntryPoint.nodeBuilder(this, buildContext).params( - SignedOutEntryPoint.Params( - sessionId = navTarget.sessionId - ) - ).build() + signedOutEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = SignedOutEntryPoint.Params( + sessionId = navTarget.sessionId, + ), + ) } NavTarget.SplashScreen -> emptyNode(buildContext) NavTarget.BugReport -> { @@ -262,11 +264,15 @@ class RootFlowNode( backstack.pop() } } - bugReportEntryPoint.nodeBuilder(this, buildContext).callback(callback).build() + bugReportEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } is NavTarget.AccountSelect -> { val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback { - override fun onSelectAccount(sessionId: SessionId) { + override fun onAccountSelected(sessionId: SessionId) { lifecycleScope.launch { if (sessionId == navTarget.currentSessionId) { // Ensure that the account selection Node is removed from the backstack @@ -287,7 +293,11 @@ class RootFlowNode( backstack.pop() } } - accountSelectEntryPoint.nodeBuilder(this, buildContext).callback(callback).build() + accountSelectEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } } } @@ -339,7 +349,7 @@ class RootFlowNode( } else { // wait for the current session to be restored val loggedInFlowNode = attachSession(latestSessionId) - if (sessionStore.getAllSessions().size > 1) { + if (sessionStore.numberOfSessions() > 1) { // Several accounts, let the user choose which one to use backstack.push( NavTarget.AccountSelect( @@ -369,7 +379,7 @@ class RootFlowNode( is PermalinkData.FallbackLink -> Unit is PermalinkData.RoomEmailInviteLink -> Unit else -> { - if (sessionStore.getAllSessions().size > 1) { + if (sessionStore.numberOfSessions() > 1) { // Several accounts, let the user choose which one to use backstack.push( NavTarget.AccountSelect( diff --git a/appnav/src/main/kotlin/io/element/android/appnav/di/TimelineBindings.kt b/appnav/src/main/kotlin/io/element/android/appnav/di/TimelineBindings.kt new file mode 100644 index 0000000000..e338f2ba8e --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/di/TimelineBindings.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.appnav.di + +import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider +import io.element.android.libraries.matrix.api.timeline.TimelineProvider + +interface TimelineBindings { + val timelineProvider: TimelineProvider + val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider +} diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt index edc2be05db..9f4187cb9c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/LoggedInNode.kt @@ -12,10 +12,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -32,18 +32,14 @@ class LoggedInNode( fun navigateToNotificationTroubleshoot() } - private fun navigateToNotificationTroubleshoot() { - plugins().forEach { - it.navigateToNotificationTroubleshoot() - } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val loggedInState = loggedInPresenter.present() LoggedInView( state = loggedInState, - navigateToNotificationTroubleshoot = ::navigateToNotificationTroubleshoot, + navigateToNotificationTroubleshoot = callback::navigateToNotificationTroubleshoot, modifier = modifier ) } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 87713ad38e..a41eb8d777 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -180,10 +180,12 @@ class RoomFlowNode( } } val params = Params(navTarget.roomAlias) - roomAliasResolverEntryPoint.nodeBuilder(this, buildContext) - .callback(callback) - .params(params) - .build() + roomAliasResolverEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } is NavTarget.JoinRoom -> { val inputs = JoinRoomEntryPoint.Inputs( @@ -193,7 +195,11 @@ class RoomFlowNode( serverNames = navTarget.serverNames, trigger = navTarget.trigger, ) - joinRoomEntryPoint.createNode(this, buildContext, inputs) + joinRoomEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inputs = inputs, + ) } is NavTarget.JoinedRoom -> { val roomFlowNodeCallback = plugins() @@ -205,10 +211,12 @@ class RoomFlowNode( } is NavTarget.JoinedSpace -> { val spaceCallback = plugins().single() - spaceEntryPoint.nodeBuilder(this, buildContext) - .inputs(SpaceEntryPoint.Inputs(roomId = navTarget.spaceId)) - .callback(spaceCallback) - .build() + spaceEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inputs = SpaceEntryPoint.Inputs(roomId = navTarget.spaceId), + callback = spaceCallback, + ) } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index a4e4540b92..419dc074fc 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -22,15 +22,16 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.RoomGraphFactory +import io.element.android.appnav.di.TimelineBindings import io.element.android.appnav.room.RoomNavigationTarget import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint -import io.element.android.features.messages.api.MessagesEntryPointNode import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.waitForChildAttached import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -46,8 +47,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.services.appnavstate.api.ActiveRoomsHolder import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import timber.log.Timber @@ -76,9 +75,9 @@ class JoinedRoomLoadedFlowNode( plugins = plugins, ), DependencyInjectionGraphOwner { interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId, serverNames: List) - fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) - fun onOpenGlobalNotificationSettings() + fun navigateToRoom(roomId: RoomId, serverNames: List) + fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) + fun navigateToGlobalNotificationSettings() } data class Inputs( @@ -87,7 +86,7 @@ class JoinedRoomLoadedFlowNode( ) : NodeInputs private val inputs: Inputs = inputs() - private val callbacks = plugins.filterIsInstance() + private val callback: Callback = callback() override val graph = roomGraphFactory.create(inputs.room) init { @@ -123,26 +122,28 @@ class JoinedRoomLoadedFlowNode( private fun createRoomDetailsNode(buildContext: BuildContext, initialTarget: RoomDetailsEntryPoint.InitialTarget): Node { val callback = object : RoomDetailsEntryPoint.Callback { - override fun onOpenGlobalNotificationSettings() { - callbacks.forEach { it.onOpenGlobalNotificationSettings() } + override fun navigateToGlobalNotificationSettings() { + callback.navigateToGlobalNotificationSettings() } - override fun onOpenRoom(roomId: RoomId, serverNames: List) { - callbacks.forEach { it.onOpenRoom(roomId, serverNames) } + override fun navigateToRoom(roomId: RoomId, serverNames: List) { + callback.navigateToRoom(roomId, serverNames) } - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - callbacks.forEach { it.onPermalinkClick(data, pushToBackstack) } + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { + callback.handlePermalinkClick(data, pushToBackstack) } - override fun forwardEvent(eventId: EventId) { - backstack.push(NavTarget.ForwardEvent(eventId)) + override fun startForwardEventFlow(eventId: EventId, fromPinnedEvents: Boolean) { + backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents)) } } - return roomDetailsEntryPoint.nodeBuilder(this, buildContext) - .params(RoomDetailsEntryPoint.Params(initialTarget)) - .callback(callback) - .build() + return roomDetailsEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = RoomDetailsEntryPoint.Params(initialTarget), + callback = callback, + ) } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -166,42 +167,50 @@ class JoinedRoomLoadedFlowNode( createSpaceNode(buildContext) } is NavTarget.ForwardEvent -> { - val timelineProvider = { MutableStateFlow(inputs.room.liveTimeline).asStateFlow() } + val timelineProvider = if (navTarget.fromPinnedEvents) { + (graph as TimelineBindings).pinnedEventsTimelineProvider + } else { + (graph as TimelineBindings).timelineProvider + } val params = ForwardEntryPoint.Params(navTarget.eventId, timelineProvider) val callback = object : ForwardEntryPoint.Callback { override fun onDone(roomIds: List) { backstack.pop() roomIds.singleOrNull()?.let { roomId -> - callbacks.forEach { it.onOpenRoom(roomId, emptyList()) } + callback.navigateToRoom(roomId, emptyList()) } } } - forwardEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + forwardEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } } } private fun createSpaceNode(buildContext: BuildContext): Node { val callback = object : SpaceEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId, viaParameters: List) { - callbacks.forEach { it.onOpenRoom(roomId, viaParameters) } + override fun navigateToRoom(roomId: RoomId, viaParameters: List) { + callback.navigateToRoom(roomId, viaParameters) } - override fun onOpenDetails() { + override fun navigateToRoomDetails() { backstack.push(NavTarget.RoomDetails) } - override fun onOpenMemberList() { + override fun navigateToRoomMemberList() { backstack.push(NavTarget.RoomMemberList) } } - return spaceEntryPoint.nodeBuilder(this, buildContext) - .inputs(SpaceEntryPoint.Inputs(roomId = inputs.room.roomId)) - .callback(callback) - .build() + return spaceEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inputs = SpaceEntryPoint.Inputs(roomId = inputs.room.roomId), + callback = callback, + ) } private fun createMessagesNode( @@ -209,33 +218,35 @@ class JoinedRoomLoadedFlowNode( navTarget: NavTarget.Messages, ): Node { val callback = object : MessagesEntryPoint.Callback { - override fun onRoomDetailsClick() { + override fun navigateToRoomDetails() { backstack.push(NavTarget.RoomDetails) } - override fun onUserDataClick(userId: UserId) { + override fun navigateToRoomMemberDetails(userId: UserId) { backstack.push(NavTarget.RoomMemberDetails(userId)) } - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - callbacks.forEach { it.onPermalinkClick(data, pushToBackstack) } + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { + callback.handlePermalinkClick(data, pushToBackstack) } - override fun forwardEvent(eventId: EventId) { - backstack.push(NavTarget.ForwardEvent(eventId)) + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { + backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents)) } - override fun openRoom(roomId: RoomId) { - callbacks.forEach { it.onOpenRoom(roomId, emptyList()) } + override fun navigateToRoom(roomId: RoomId) { + callback.navigateToRoom(roomId, emptyList()) } } val params = MessagesEntryPoint.Params( MessagesEntryPoint.InitialTarget.Messages(navTarget.focusedEventId) ) - return messagesEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + return messagesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } sealed interface NavTarget : Parcelable { @@ -257,7 +268,7 @@ class JoinedRoomLoadedFlowNode( data class RoomMemberDetails(val userId: UserId) : NavTarget @Parcelize - data class ForwardEvent(val eventId: EventId) : NavTarget + data class ForwardEvent(val eventId: EventId, val fromPinnedEvents: Boolean) : NavTarget @Parcelize data object RoomNotificationSettings : NavTarget @@ -267,7 +278,7 @@ class JoinedRoomLoadedFlowNode( val messageNode = waitForChildAttached { navTarget -> navTarget is NavTarget.Messages } - (messageNode as? MessagesEntryPointNode)?.attachThread(threadId, focusedEventId) + (messageNode as? MessagesEntryPoint.NodeProxy)?.attachThread(threadId, focusedEventId) } @Composable diff --git a/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt index c9eb28397e..e560593399 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt @@ -19,8 +19,10 @@ import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper import com.google.common.truth.Truth.assertThat import io.element.android.appnav.di.RoomGraphFactory import io.element.android.appnav.room.RoomNavigationTarget +import io.element.android.appnav.room.joined.FakeJoinedRoomLoadedFlowNodeCallback import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode import io.element.android.features.forward.api.ForwardEntryPoint +import io.element.android.features.forward.test.FakeForwardEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.space.api.SpaceEntryPoint @@ -48,29 +50,20 @@ class JoinedRoomLoadedFlowNodeTest { @get:Rule val mainDispatcherRule = MainDispatcherRule() - private class FakeMessagesEntryPoint : MessagesEntryPoint, MessagesEntryPoint.NodeBuilder { - var buildContext: BuildContext? = null + private class FakeMessagesEntryPoint : MessagesEntryPoint { var nodeId: String? = null var parameters: MessagesEntryPoint.Params? = null var callback: MessagesEntryPoint.Callback? = null - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder { - this.buildContext = buildContext - return this - } - - override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: MessagesEntryPoint.Params, + callback: MessagesEntryPoint.Callback, + ): Node { parameters = params - return this - } - - override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder { this.callback = callback - return this - } - - override fun build(): Node { - return node(buildContext!!) {}.also { + return node(buildContext) {}.also { nodeId = it.id } } @@ -85,54 +78,26 @@ class JoinedRoomLoadedFlowNodeTest { private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint { var nodeId: String? = null - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDetailsEntryPoint.NodeBuilder { - return object : RoomDetailsEntryPoint.NodeBuilder { - override fun params(params: RoomDetailsEntryPoint.Params): RoomDetailsEntryPoint.NodeBuilder { - return this - } - - override fun callback(callback: RoomDetailsEntryPoint.Callback): RoomDetailsEntryPoint.NodeBuilder { - return this - } - - override fun build(): Node { - return node(buildContext) {}.also { - nodeId = it.id - } - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: RoomDetailsEntryPoint.Params, + callback: RoomDetailsEntryPoint.Callback, + ) = node(buildContext) {}.also { + nodeId = it.id } } private class FakeSpaceEntryPoint : SpaceEntryPoint { var nodeId: String? = null - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SpaceEntryPoint.NodeBuilder { - return object : SpaceEntryPoint.NodeBuilder { - override fun inputs(inputs: SpaceEntryPoint.Inputs): SpaceEntryPoint.NodeBuilder { - return this - } - - override fun callback(callback: SpaceEntryPoint.Callback): SpaceEntryPoint.NodeBuilder { - return this - } - - override fun build(): Node { - return node(buildContext) {}.also { - nodeId = it.id - } - } - } - } - } - - private class FakeForwardEntryPoint : ForwardEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ForwardEntryPoint.NodeBuilder { - return object : ForwardEntryPoint.NodeBuilder { - override fun params(params: ForwardEntryPoint.Params) = this - override fun callback(callback: ForwardEntryPoint.Callback) = this - override fun build() = node(buildContext) {} - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: SpaceEntryPoint.Inputs, + callback: SpaceEntryPoint.Callback, + ) = node(buildContext) {}.also { + nodeId = it.id } } @@ -165,7 +130,7 @@ class JoinedRoomLoadedFlowNodeTest { val fakeMessagesEntryPoint = FakeMessagesEntryPoint() val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root()) val roomFlowNode = createJoinedRoomLoadedFlowNode( - plugins = listOf(inputs), + plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()), messagesEntryPoint = fakeMessagesEntryPoint, ) // WHEN @@ -185,7 +150,7 @@ class JoinedRoomLoadedFlowNodeTest { val spaceEntryPoint = FakeSpaceEntryPoint() val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root()) val roomFlowNode = createJoinedRoomLoadedFlowNode( - plugins = listOf(inputs), + plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()), spaceEntryPoint = spaceEntryPoint, ) // WHEN @@ -206,13 +171,13 @@ class JoinedRoomLoadedFlowNodeTest { val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint() val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root()) val roomFlowNode = createJoinedRoomLoadedFlowNode( - plugins = listOf(inputs), + plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()), messagesEntryPoint = fakeMessagesEntryPoint, roomDetailsEntryPoint = fakeRoomDetailsEntryPoint, ) val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper() // WHEN - fakeMessagesEntryPoint.callback?.onRoomDetailsClick() + fakeMessagesEntryPoint.callback?.navigateToRoomDetails() // THEN roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED) val roomDetailsNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.RoomDetails)!! @@ -228,7 +193,7 @@ class JoinedRoomLoadedFlowNodeTest { val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root()) val activeRoomsHolder = ActiveRoomsHolder() val roomFlowNode = createJoinedRoomLoadedFlowNode( - plugins = listOf(inputs), + plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()), messagesEntryPoint = fakeMessagesEntryPoint, roomDetailsEntryPoint = fakeRoomDetailsEntryPoint, activeRoomsHolder = activeRoomsHolder, @@ -253,7 +218,7 @@ class JoinedRoomLoadedFlowNodeTest { addRoom(room) } val roomFlowNode = createJoinedRoomLoadedFlowNode( - plugins = listOf(inputs), + plugins = listOf(inputs, FakeJoinedRoomLoadedFlowNodeCallback()), messagesEntryPoint = fakeMessagesEntryPoint, roomDetailsEntryPoint = fakeRoomDetailsEntryPoint, activeRoomsHolder = activeRoomsHolder, diff --git a/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.kt new file mode 100644 index 0000000000..0e2e0c17f8 --- /dev/null +++ b/appnav/src/test/kotlin/io/element/android/appnav/room/joined/FakeJoinedRoomLoadedFlowNodeCallback.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.appnav.room.joined + +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeJoinedRoomLoadedFlowNodeCallback : JoinedRoomLoadedFlowNode.Callback { + override fun navigateToRoom(roomId: RoomId, serverNames: List) = lambdaError() + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() + override fun navigateToGlobalNotificationSettings() = lambdaError() +} diff --git a/fastlane/metadata/android/en-US/changelogs/202511000.txt b/fastlane/metadata/android/en-US/changelogs/202511000.txt new file mode 100644 index 0000000000..8afd7460fc --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/202511000.txt @@ -0,0 +1,2 @@ +Main changes in this version: fixes an issue that prevented Element Call notifications from being displayed sometimes. +Full changelog: https://github.com/element-hq/element-x-android/releases diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt index 7604dbae18..a7302c9730 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenter.kt @@ -201,7 +201,7 @@ class CallScreenPresenter( userAgent = userAgent, isCallActive = isWidgetLoaded, isInWidgetMode = isInWidgetMode, - eventSink = { handleEvents(it) }, + eventSink = ::handleEvents, ) } diff --git a/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroes/api/ChangeRoomMemberRolesEntryPoint.kt b/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt similarity index 71% rename from features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroes/api/ChangeRoomMemberRolesEntryPoint.kt rename to features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt index b6f7680b38..7905cdc0ae 100644 --- a/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroes/api/ChangeRoomMemberRolesEntryPoint.kt +++ b/features/changeroommemberroles/api/src/main/kotlin/io/element/android/features/changeroommemberroles/api/ChangeRoomMemberRolesEntryPoint.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroes.api +package io.element.android.features.changeroommemberroles.api import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -15,13 +15,12 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.JoinedRoom fun interface ChangeRoomMemberRolesEntryPoint : FeatureEntryPoint { - fun builder(parentNode: Node, buildContext: BuildContext): Builder - - interface Builder { - fun room(room: JoinedRoom): Builder - fun listType(changeRoomMemberRolesListType: ChangeRoomMemberRolesListType): Builder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + room: JoinedRoom, + listType: ChangeRoomMemberRolesListType, + ): Node interface NodeProxy { val roomId: RoomId diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt index 1b9c790b25..9edd20b549 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.appyx.launchMolecule import io.element.android.libraries.architecture.inputs diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt index 2c3f77f208..a7556ef8ca 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRoomMemberRolesRootNode.kt @@ -20,8 +20,8 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appnav.di.RoomGraphFactory -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs diff --git a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt index 53fb89720b..e76333cda7 100644 --- a/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt +++ b/features/changeroommemberroles/impl/src/main/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPoint.kt @@ -10,37 +10,25 @@ package io.element.android.features.changeroommemberroles.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.room.JoinedRoom @ContributesBinding(SessionScope::class) class DefaultChangeRoomMemberRolesEntyPoint : ChangeRoomMemberRolesEntryPoint { - override fun builder(parentNode: Node, buildContext: BuildContext): ChangeRoomMemberRolesEntryPoint.Builder { - return object : ChangeRoomMemberRolesEntryPoint.Builder { - private lateinit var changeRoomMemberRolesListType: ChangeRoomMemberRolesListType - private lateinit var room: JoinedRoom - - override fun room(room: JoinedRoom): ChangeRoomMemberRolesEntryPoint.Builder { - this.room = room - return this - } - - override fun listType(changeRoomMemberRolesListType: ChangeRoomMemberRolesListType): ChangeRoomMemberRolesEntryPoint.Builder { - this.changeRoomMemberRolesListType = changeRoomMemberRolesListType - return this - } - - override fun build(): Node { - return parentNode.createNode( - buildContext = buildContext, - plugins = listOf( - ChangeRoomMemberRolesRootNode.Inputs(joinedRoom = room, listType = changeRoomMemberRolesListType), - ) - ) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + room: JoinedRoom, + listType: ChangeRoomMemberRolesListType, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + ChangeRoomMemberRolesRootNode.Inputs(joinedRoom = room, listType = listType), + ) + ) } } diff --git a/features/changeroommemberroles/impl/src/main/res/values-de/translations.xml b/features/changeroommemberroles/impl/src/main/res/values-de/translations.xml index 88d795d614..09a04a4d35 100644 --- a/features/changeroommemberroles/impl/src/main/res/values-de/translations.xml +++ b/features/changeroommemberroles/impl/src/main/res/values-de/translations.xml @@ -33,7 +33,7 @@ "Mitglieder" "Du hast nicht gespeicherte Änderungen." "Änderungen speichern?" - "In diesem Chat gibt es keine gesperrten Nutzer." + "Es gibt keine gesperrten Nutzer." "%1$d Person" "%1$d Personen" diff --git a/features/changeroommemberroles/impl/src/main/res/values-et/translations.xml b/features/changeroommemberroles/impl/src/main/res/values-et/translations.xml index a43a9a89d0..ca7faadde0 100644 --- a/features/changeroommemberroles/impl/src/main/res/values-et/translations.xml +++ b/features/changeroommemberroles/impl/src/main/res/values-et/translations.xml @@ -33,7 +33,7 @@ "Liikmed" "Sul on salvestamata muudatusi" "Kas salvestame muudatused?" - "Jututoas pole suhtluskeeluga kasutajaid" + "Suhtluskeeluga kasutajaid pole" "%1$d osaleja" "%1$d osalejat" diff --git a/features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml b/features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml index 8d5c6e8502..88ff80b71f 100644 --- a/features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml +++ b/features/changeroommemberroles/impl/src/main/res/values-sk/translations.xml @@ -33,7 +33,7 @@ "Členovia" "Máte neuložené zmeny." "Uložiť zmeny?" - "V tejto miestnosti nie sú žiadni zakázaní používatelia." + "Neexistujú žiadni zablokovaní používatelia." "%1$d osoba" "%1$d osoby" diff --git a/features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml b/features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml index 2ec5ec64c5..152244e066 100644 --- a/features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/changeroommemberroles/impl/src/main/res/values-zh-rTW/translations.xml @@ -33,7 +33,7 @@ "成員" "您有尚未儲存的變更" "是否儲存變更?" - "此聊天室沒有黑名單。" + "沒有被封鎖的使用者。" "%1$d 位夥伴" diff --git a/features/changeroommemberroles/impl/src/main/res/values/localazy.xml b/features/changeroommemberroles/impl/src/main/res/values/localazy.xml index 456426726a..43f6dc10f8 100644 --- a/features/changeroommemberroles/impl/src/main/res/values/localazy.xml +++ b/features/changeroommemberroles/impl/src/main/res/values/localazy.xml @@ -33,7 +33,7 @@ "Members" "You have unsaved changes." "Save changes?" - "There are no banned users in this room." + "There are no banned users." "%1$d person" "%1$d people" diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt index 5a47f52e89..3da57d3380 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt +++ b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/ChangeRolesNodeTest.kt @@ -8,7 +8,7 @@ package io.element.android.features.changeroommemberroles.impl import com.google.common.truth.Truth.assertThat -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.api.room.RoomMember import org.junit.Test diff --git a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt index 621af8edaf..84e53c0cb6 100644 --- a/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt +++ b/features/changeroommemberroles/impl/src/test/kotlin/io/element/android/features/changeroommemberroles/impl/DefaultChangeRoomMemberRolesEntyPointTest.kt @@ -10,7 +10,7 @@ package io.element.android.features.changeroommemberroles.impl import androidx.test.ext.junit.runners.AndroidJUnit4 import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -31,10 +31,12 @@ class DefaultChangeRoomMemberRolesEntyPointTest { } val room = FakeJoinedRoom() val listType = ChangeRoomMemberRolesListType.Admins - val result = entryPoint.builder(parentNode, BuildContext.root(null)) - .room(FakeJoinedRoom()) - .listType(listType) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + room = FakeJoinedRoom(), + listType = listType, + ) assertThat(result).isInstanceOf(ChangeRoomMemberRolesRootNode::class.java) // Search for the Inputs plugin val input = result.plugins.filterIsInstance().single() diff --git a/features/changeroommemberroles/test/build.gradle.kts b/features/changeroommemberroles/test/build.gradle.kts new file mode 100644 index 0000000000..4d85d83c90 --- /dev/null +++ b/features/changeroommemberroles/test/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.changeroommemberroles.test" +} + +dependencies { + implementation(projects.features.changeroommemberroles.api) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.tests.testutils) +} diff --git a/features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt b/features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt new file mode 100644 index 0000000000..16b404ab5e --- /dev/null +++ b/features/changeroommemberroles/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.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.features.changeroommemberroles.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType +import io.element.android.libraries.matrix.api.room.JoinedRoom +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeChangeRoomMemberRolesEntryPoint : ChangeRoomMemberRolesEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + room: JoinedRoom, + listType: ChangeRoomMemberRolesListType, + ): Node { + lambdaError() + } +} diff --git a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt index 5fc71f2a3b..c9763f183d 100644 --- a/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt +++ b/features/createroom/api/src/main/kotlin/io/element/android/features/createroom/api/CreateRoomEntryPoint.kt @@ -14,12 +14,11 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.RoomId interface CreateRoomEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { fun onRoomCreated(roomId: RoomId) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt index 8f46103ba5..809edc933e 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/CreateRoomFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.replace import dev.zacsweers.metro.Assisted @@ -24,6 +23,7 @@ import io.element.android.features.createroom.impl.addpeople.AddPeopleNode import io.element.android.features.createroom.impl.configureroom.ConfigureRoomNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -42,9 +42,7 @@ class CreateRoomFlowNode( buildContext = buildContext, plugins = plugins ) { - private fun onRoomCreated(roomId: RoomId) { - plugins().forEach { it.onRoomCreated(roomId) } - } + private val callback: CreateRoomEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { @@ -60,7 +58,7 @@ class CreateRoomFlowNode( val inputs = AddPeopleNode.Inputs(navTarget.roomId) val callback: AddPeopleNode.Callback = object : AddPeopleNode.Callback { override fun onFinish() { - onRoomCreated(navTarget.roomId) + callback.onRoomCreated(navTarget.roomId) } } createNode(buildContext, plugins = listOf(inputs, callback)) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt index 3ee0a92338..35b05a548d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.createroom.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.features.createroom.api.CreateRoomEntryPoint import io.element.android.libraries.architecture.createNode @@ -17,18 +16,11 @@ import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultCreateRoomEntryPoint : CreateRoomEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreateRoomEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : CreateRoomEntryPoint.NodeBuilder { - override fun callback(callback: CreateRoomEntryPoint.Callback): CreateRoomEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: CreateRoomEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt index 9ea89912cb..5db56043d9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/addpeople/AddPeopleNode.kt @@ -12,13 +12,13 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.invitepeople.api.InvitePeoplePresenter import io.element.android.features.invitepeople.api.InvitePeopleRenderer import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -39,10 +39,7 @@ class AddPeopleNode( fun onFinish() } - private fun onFinish() { - plugins().forEach { it.onFinish() } - } - + private val callback: Callback = callback() private val roomId = inputs().roomId private val invitePeoplePresenter = invitePeoplePresenterFactory.create( joinedRoom = null, @@ -54,7 +51,7 @@ class AddPeopleNode( val state = invitePeoplePresenter.present() AddPeopleView( state = state, - onFinish = ::onFinish, + onFinish = callback::onFinish, ) { invitePeopleRenderer.Render(state, Modifier) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt index 9e721d28bd..3c57c0b59d 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomNode.kt @@ -13,11 +13,11 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.services.analytics.api.AnalyticsService @@ -42,9 +42,7 @@ class ConfigureRoomNode( ) } - private fun onCreateRoomSuccess(roomId: RoomId) { - plugins().forEach { it.onCreateRoomSuccess(roomId) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -53,7 +51,7 @@ class ConfigureRoomNode( state = state, modifier = modifier, onBackClick = this::navigateUp, - onCreateRoomSuccess = ::onCreateRoomSuccess, + onCreateRoomSuccess = callback::onCreateRoomSuccess, ) } } diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPointTest.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPointTest.kt index 61c7c052c5..4690b5b7f3 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPointTest.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/DefaultCreateRoomEntryPointTest.kt @@ -38,9 +38,11 @@ class DefaultCreateRoomEntryPointTest { val callback = object : CreateRoomEntryPoint.Callback { override fun onRoomCreated(roomId: RoomId) = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result.plugins).contains(callback) } } diff --git a/features/createroom/test/build.gradle.kts b/features/createroom/test/build.gradle.kts new file mode 100644 index 0000000000..13c579e5da --- /dev/null +++ b/features/createroom/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.createroom.test" +} + +dependencies { + implementation(projects.features.createroom.api) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) +} diff --git a/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/api/FakeCreateRoomEntryPoint.kt b/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/api/FakeCreateRoomEntryPoint.kt new file mode 100644 index 0000000000..1f5a217537 --- /dev/null +++ b/features/createroom/test/src/main/kotlin/io/element/android/features/createroom/api/FakeCreateRoomEntryPoint.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.createroom.api + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeCreateRoomEntryPoint : CreateRoomEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: CreateRoomEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/features/deactivation/test/build.gradle.kts b/features/deactivation/test/build.gradle.kts new file mode 100644 index 0000000000..57ba7f4421 --- /dev/null +++ b/features/deactivation/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.deactivation.test" +} + +dependencies { + implementation(projects.features.deactivation.api) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) +} diff --git a/features/deactivation/test/src/main/kotlin/io/element/android/features/deactivation/test/FakeAccountDeactivationEntryPoint.kt b/features/deactivation/test/src/main/kotlin/io/element/android/features/deactivation/test/FakeAccountDeactivationEntryPoint.kt new file mode 100644 index 0000000000..769f07f1a4 --- /dev/null +++ b/features/deactivation/test/src/main/kotlin/io/element/android/features/deactivation/test/FakeAccountDeactivationEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.deactivation.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.deactivation.api.AccountDeactivationEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeAccountDeactivationEntryPoint : AccountDeactivationEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + ): Node { + lambdaError() + } +} diff --git a/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt b/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt index d822c0adb8..960c5362c2 100644 --- a/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt +++ b/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt @@ -17,12 +17,6 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.timeline.TimelineProvider interface ForwardEntryPoint : FeatureEntryPoint { - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } - interface Callback : Plugin { fun onDone(roomIds: List) } @@ -32,5 +26,10 @@ interface ForwardEntryPoint : FeatureEntryPoint { val timelineProvider: TimelineProvider, ) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node } diff --git a/features/forward/impl/build.gradle.kts b/features/forward/impl/build.gradle.kts index 32da0ed7a2..0364ffadd8 100644 --- a/features/forward/impl/build.gradle.kts +++ b/features/forward/impl/build.gradle.kts @@ -31,8 +31,10 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.matrix.api) implementation(projects.libraries.roomselect.api) + implementation(projects.libraries.uiStrings) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.roomselect.test) testImplementation(projects.libraries.testtags) } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt index 55eede7b57..ab472fefbc 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.forward.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.libraries.architecture.createNode @@ -17,26 +16,21 @@ import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultForwardEntryPoint : ForwardEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ForwardEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : ForwardEntryPoint.NodeBuilder { - override fun params(params: ForwardEntryPoint.Params): ForwardEntryPoint.NodeBuilder { - plugins += ForwardMessagesNode.Inputs( + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: ForwardEntryPoint.Params, + callback: ForwardEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + ForwardMessagesNode.Inputs( eventId = params.eventId, timelineProvider = params.timelineProvider, - ) - return this - } - - override fun callback(callback: ForwardEntryPoint.Callback): ForwardEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + ), + callback, + ) + ) } } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt index 27e95898d2..a38f71264b 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt @@ -22,6 +22,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.EventId @@ -55,8 +56,8 @@ class ForwardMessagesNode( ) : NodeInputs private val inputs = inputs() + private val callback: ForwardEntryPoint.Callback = callback() private val presenter = presenterFactory.create(inputs.eventId.value, inputs.timelineProvider) - private val callbacks = plugins.filterIsInstance() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : RoomSelectEntryPoint.Callback { @@ -65,14 +66,16 @@ class ForwardMessagesNode( } override fun onCancel() { - onForwardDone(emptyList()) + callback.onDone(emptyList()) } } - return roomSelectEntryPoint.nodeBuilder(this, buildContext) - .callback(callback) - .params(RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward)) - .build() + return roomSelectEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Forward), + callback = callback, + ) } @Composable @@ -86,12 +89,8 @@ class ForwardMessagesNode( val state = presenter.present() ForwardMessagesView( state = state, - onForwardSuccess = ::onForwardDone, + onForwardSuccess = callback::onDone, ) } } - - private fun onForwardDone(roomIds: List) { - callbacks.forEach { it.onDone(roomIds) } - } } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt index 63fbb0bc7c..b1e9534533 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.getActiveTimeline import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import timber.log.Timber @AssistedInject class ForwardMessagesPresenter( @@ -54,7 +55,7 @@ class ForwardMessagesPresenter( return ForwardMessagesState( forwardAction = forwardingActionState.value, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } @@ -63,7 +64,11 @@ class ForwardMessagesPresenter( roomIds: List, ) = launch { suspend { - timelineProvider.getActiveTimeline().forwardEvent(eventId, roomIds).getOrThrow() + timelineProvider.getActiveTimeline().forwardEvent(eventId, roomIds) + .onFailure { + Timber.e(it, "Error while forwarding event") + } + .getOrThrow() roomIds }.runCatchingUpdatingState(forwardingActionState) } diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt index 214c795851..3e092c70fa 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesView.kt @@ -8,11 +8,13 @@ package io.element.android.features.forward.impl import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.ui.strings.CommonStrings @Composable fun ForwardMessagesView( @@ -24,6 +26,9 @@ fun ForwardMessagesView( onSuccess = { onForwardSuccess(it) }, + errorMessage = { + stringResource(id = CommonStrings.error_unknown) + }, onErrorDismiss = { state.eventSink(ForwardMessagesEvents.ClearError) }, diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt index dabb8af4a1..28c5f668e6 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt @@ -9,14 +9,13 @@ package io.element.android.features.forward.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.forward.api.ForwardEntryPoint import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.timeline.FakeTimelineProvider -import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -38,11 +37,7 @@ class DefaultForwardEntryPointTest { buildContext = buildContext, plugins = plugins, presenterFactory = { _, _ -> createForwardMessagesPresenter() }, - roomSelectEntryPoint = object : RoomSelectEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder { - lambdaError() - } - } + roomSelectEntryPoint = FakeRoomSelectEntryPoint(), ) } val callback = object : ForwardEntryPoint.Callback { @@ -52,10 +47,12 @@ class DefaultForwardEntryPointTest { eventId = AN_EVENT_ID, timelineProvider = FakeTimelineProvider(), ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(ForwardMessagesNode::class.java) assertThat(result.plugins).contains( ForwardMessagesNode.Inputs( diff --git a/features/forward/test/build.gradle.kts b/features/forward/test/build.gradle.kts new file mode 100644 index 0000000000..a669fe5128 --- /dev/null +++ b/features/forward/test/build.gradle.kts @@ -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. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.forward.test" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.features.forward.api) + implementation(projects.tests.testutils) +} diff --git a/features/forward/test/src/main/kotlin/io/element/android/features/forward/test/FakeForwardEntryPoint.kt b/features/forward/test/src/main/kotlin/io/element/android/features/forward/test/FakeForwardEntryPoint.kt new file mode 100644 index 0000000000..4882461ad0 --- /dev/null +++ b/features/forward/test/src/main/kotlin/io/element/android/features/forward/test/FakeForwardEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.forward.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.forward.api.ForwardEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeForwardEntryPoint : ForwardEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: ForwardEntryPoint.Params, + callback: ForwardEntryPoint.Callback, + ): Node = lambdaError() +} 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 6552a3b360..7c045f0150 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 @@ -110,9 +110,12 @@ class FtueFlowNode( defaultFtueService.updateFtueStep() } } - lockScreenEntryPoint.nodeBuilder(this, buildContext, LockScreenEntryPoint.Target.Setup) - .callback(callback) - .build() + lockScreenEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + navTarget = LockScreenEntryPoint.Target.Setup, + callback = callback, + ) } } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/FtueSessionVerificationFlowNode.kt index 02a27d381d..13da4a0748 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 @@ -15,7 +15,6 @@ import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import 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 @@ -29,6 +28,7 @@ import io.element.android.features.securebackup.api.SecureBackupEntryPoint import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.designsystem.utils.OpenUrlInTabView import io.element.android.libraries.di.SessionScope @@ -69,6 +69,8 @@ class FtueSessionVerificationFlowNode( fun onDone() } + private val callback: Callback = callback() + private val secureBackupEntryPointCallback = object : SecureBackupEntryPoint.Callback { override fun onDone() { lifecycleScope.launch { @@ -82,62 +84,67 @@ class FtueSessionVerificationFlowNode( return when (navTarget) { is NavTarget.Root -> { val callback = object : ChooseSelfVerificationModeNode.Callback { - override fun onUseAnotherDevice() { + override fun navigateToUseAnotherDevice() { backstack.push(NavTarget.UseAnotherDevice) } - override fun onUseRecoveryKey() { + override fun navigateToUseRecoveryKey() { backstack.push(NavTarget.EnterRecoveryKey) } - override fun onResetKey() { + override fun navigateToResetKey() { backstack.push(NavTarget.ResetIdentity) } - override fun onLearnMoreAboutEncryption() { + override fun navigateToLearnMoreAboutEncryption() { learnMoreUrl.value = LearnMoreConfig.DEVICE_VERIFICATION_URL } } - createNode(buildContext, plugins = listOf(callback)) } is NavTarget.UseAnotherDevice -> { - outgoingVerificationEntryPoint.nodeBuilder(this, buildContext) - .params(OutgoingVerificationEntryPoint.Params( + outgoingVerificationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = OutgoingVerificationEntryPoint.Params( showDeviceVerifiedScreen = true, verificationRequest = VerificationRequest.Outgoing.CurrentSession, - )) - .callback(object : OutgoingVerificationEntryPoint.Callback { + ), + callback = object : OutgoingVerificationEntryPoint.Callback { override fun onDone() { - plugins().forEach { it.onDone() } + callback.onDone() } override fun onBack() { backstack.pop() } - override fun onLearnMoreAboutEncryption() { + override fun navigateToLearnMoreAboutEncryption() { // Note that this callback is never called. The "Learn more" link is not displayed // for the self session interactive verification. } - }) - .build() + } + ) } is NavTarget.EnterRecoveryKey -> { - secureBackupEntryPoint.nodeBuilder(this, buildContext) - .params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey)) - .callback(secureBackupEntryPointCallback) - .build() + secureBackupEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey), + callback = secureBackupEntryPointCallback + ) } is NavTarget.ResetIdentity -> { - secureBackupEntryPoint.nodeBuilder(this, buildContext) - .params(SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity)) - .callback(object : SecureBackupEntryPoint.Callback { + secureBackupEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity), + callback = object : SecureBackupEntryPoint.Callback { override fun onDone() { - plugins().forEach { it.onDone() } + callback.onDone() } - }) - .build() + }, + ) } } } diff --git a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt index 99409ac2d2..ac2aa09b22 100644 --- a/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt +++ b/features/ftue/impl/src/main/kotlin/io/element/android/features/ftue/impl/sessionverification/choosemode/ChooseSelfVerificationModeNode.kt @@ -12,12 +12,12 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -29,13 +29,13 @@ class ChooseSelfVerificationModeNode( private val directLogoutView: DirectLogoutView, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onUseAnotherDevice() - fun onUseRecoveryKey() - fun onResetKey() - fun onLearnMoreAboutEncryption() + fun navigateToUseAnotherDevice() + fun navigateToUseRecoveryKey() + fun navigateToResetKey() + fun navigateToLearnMoreAboutEncryption() } - private val callback = plugins().first() + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -43,10 +43,10 @@ class ChooseSelfVerificationModeNode( ChooseSelfVerificationModeView( state = state, - onUseAnotherDevice = callback::onUseAnotherDevice, - onUseRecoveryKey = callback::onUseRecoveryKey, - onResetKey = callback::onResetKey, - onLearnMore = callback::onLearnMoreAboutEncryption, + onUseAnotherDevice = callback::navigateToUseAnotherDevice, + onUseRecoveryKey = callback::navigateToUseRecoveryKey, + onResetKey = callback::navigateToResetKey, + onLearnMore = callback::navigateToLearnMoreAboutEncryption, modifier = modifier, ) diff --git a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueEntryPointTest.kt b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueEntryPointTest.kt index 3a8ed11ea2..b7036ee69b 100644 --- a/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueEntryPointTest.kt +++ b/features/ftue/impl/src/test/kotlin/io/element/android/features/ftue/impl/DefaultFtueEntryPointTest.kt @@ -7,13 +7,11 @@ package io.element.android.features.ftue.impl -import android.content.Context -import android.content.Intent import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.lockscreen.api.LockScreenEntryPoint +import io.element.android.features.lockscreen.test.FakeLockScreenEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -36,19 +34,7 @@ class DefaultFtueEntryPointTest { plugins = plugins, analyticsEntryPoint = { _, _ -> lambdaError() }, defaultFtueService = createDefaultFtueService(), - lockScreenEntryPoint = object : LockScreenEntryPoint { - override fun nodeBuilder( - parentNode: com.bumble.appyx.core.node.Node, - buildContext: BuildContext, - navTarget: LockScreenEntryPoint.Target - ): LockScreenEntryPoint.NodeBuilder { - lambdaError() - } - - override fun pinUnlockIntent(context: Context): Intent { - lambdaError() - } - }, + lockScreenEntryPoint = FakeLockScreenEntryPoint(), ) } val result = entryPoint.createNode(parentNode, BuildContext.root(null)) diff --git a/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt b/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt index 9beb147568..91196dc2ad 100644 --- a/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt +++ b/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt @@ -14,19 +14,19 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.RoomId interface HomeEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { - fun onRoomClick(roomId: RoomId) - fun onStartChatClick() - fun onSettingsClick() - fun onSetUpRecoveryClick() - fun onSessionConfirmRecoveryKeyClick() - fun onRoomSettingsClick(roomId: RoomId) - fun onReportBugClick() + fun navigateToRoom(roomId: RoomId) + fun navigateToCreateRoom() + fun navigateToSettings() + fun navigateToSetUpRecovery() + fun navigateToEnterRecoveryKey() + fun navigateToRoomSettings(roomId: RoomId) + fun navigateToBugReport() } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPoint.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPoint.kt index 39c647f8f8..73b928ce02 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPoint.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.home.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.home.api.HomeEntryPoint @@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultHomeEntryPoint : HomeEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): HomeEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : HomeEntryPoint.NodeBuilder { - override fun callback(callback: HomeEntryPoint.Callback): HomeEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: HomeEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index cb205960fd..5380c51295 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -21,7 +21,6 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -29,8 +28,8 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.features.home.api.HomeEntryPoint import io.element.android.features.home.impl.components.RoomListMenuAction import io.element.android.features.home.impl.model.RoomListRoomSummary @@ -44,6 +43,7 @@ import io.element.android.features.reportroom.api.ReportRoomEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.launchMolecule +import io.element.android.libraries.architecture.callback import io.element.android.libraries.deeplink.api.usecase.InviteFriendsUseCase import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient @@ -78,6 +78,7 @@ class HomeFlowNode( buildContext = buildContext, plugins = plugins ) { + private val callback: HomeEntryPoint.Callback = callback() private val stateFlow = launchMolecule { presenter.present() } override fun onBuilt() { @@ -115,35 +116,11 @@ class HomeFlowNode( data class SelectNewOwnersWhenLeavingRoom(val roomId: RoomId) : NavTarget } - private fun onRoomClick(roomId: RoomId) { - plugins().forEach { it.onRoomClick(roomId) } - } - - private fun onOpenSettings() { - plugins().forEach { it.onSettingsClick() } - } - - private fun onStartChatClick() { - plugins().forEach { it.onStartChatClick() } - } - - private fun onSetUpRecoveryClick() { - plugins().forEach { it.onSetUpRecoveryClick() } - } - - private fun onSessionConfirmRecoveryKeyClick() { - plugins().forEach { it.onSessionConfirmRecoveryKeyClick() } - } - - private fun onRoomSettingsClick(roomId: RoomId) { - plugins().forEach { it.onRoomSettingsClick(roomId) } - } - - private fun onReportRoomClick(roomId: RoomId) { + private fun navigateToReportRoom(roomId: RoomId) { backstack.push(NavTarget.ReportRoom(roomId)) } - private fun onDeclineInviteAndBlockUserClick(roomSummary: RoomListRoomSummary) { + private fun navigateToDeclineInviteAndBlockUser(roomSummary: RoomListRoomSummary) { backstack.push(NavTarget.DeclineInviteAndBlockUser(roomSummary.toInviteData())) } @@ -153,12 +130,12 @@ class HomeFlowNode( inviteFriendsUseCase.execute(activity) } RoomListMenuAction.ReportBug -> { - plugins().forEach { it.onReportBugClick() } + callback.navigateToBugReport() } } } - private fun onSelectNewOwnersWhenLeavingRoom(roomId: RoomId) { + private fun navigateToSelectNewOwnersWhenLeavingRoom(roomId: RoomId) { backstack.push(NavTarget.SelectNewOwnersWhenLeavingRoom(roomId)) } @@ -172,20 +149,20 @@ class HomeFlowNode( val activity = requireNotNull(LocalActivity.current) HomeView( homeState = state, - onRoomClick = this::onRoomClick, - onSettingsClick = this::onOpenSettings, - onStartChatClick = this::onStartChatClick, - onSetUpRecoveryClick = this::onSetUpRecoveryClick, - onConfirmRecoveryKeyClick = this::onSessionConfirmRecoveryKeyClick, - onRoomSettingsClick = this::onRoomSettingsClick, + onRoomClick = callback::navigateToRoom, + onSettingsClick = callback::navigateToSettings, + onStartChatClick = callback::navigateToCreateRoom, + onSetUpRecoveryClick = callback::navigateToSetUpRecovery, + onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey, + onRoomSettingsClick = callback::navigateToRoomSettings, onMenuActionClick = { onMenuActionClick(activity, it) }, - onReportRoomClick = this::onReportRoomClick, - onDeclineInviteAndBlockUser = this::onDeclineInviteAndBlockUserClick, + onReportRoomClick = ::navigateToReportRoom, + onDeclineInviteAndBlockUser = ::navigateToDeclineInviteAndBlockUser, modifier = modifier, acceptDeclineInviteView = { acceptDeclineInviteView.Render( state = state.roomListState.acceptDeclineInviteState, - onAcceptInviteSuccess = this::onRoomClick, + onAcceptInviteSuccess = callback::navigateToRoom, onDeclineInviteSuccess = { }, modifier = Modifier ) @@ -193,7 +170,7 @@ class HomeFlowNode( leaveRoomView = { leaveRoomRenderer.Render( state = state.roomListState.leaveRoomState, - onSelectNewOwners = this::onSelectNewOwnersWhenLeavingRoom, + onSelectNewOwners = ::navigateToSelectNewOwnersWhenLeavingRoom, modifier = Modifier ) } @@ -209,14 +186,28 @@ class HomeFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - is NavTarget.ReportRoom -> reportRoomEntryPoint.createNode(this, buildContext, navTarget.roomId) - is NavTarget.DeclineInviteAndBlockUser -> declineInviteAndBlockUserEntryPoint.createNode(this, buildContext, navTarget.inviteData) + is NavTarget.ReportRoom -> { + reportRoomEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + roomId = navTarget.roomId, + ) + } + is NavTarget.DeclineInviteAndBlockUser -> { + declineInviteAndBlockUserEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inviteData = navTarget.inviteData, + ) + } is NavTarget.SelectNewOwnersWhenLeavingRoom -> { val room = runBlocking { matrixClient.getJoinedRoom(navTarget.roomId) } ?: error("Room ${navTarget.roomId} not found") - changeRoomMemberRolesEntryPoint.builder(this, buildContext) - .room(room) - .listType(ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving) - .build() + changeRoomMemberRolesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + room = room, + listType = ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving, + ) } NavTarget.Root -> rootNode(buildContext) } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt index 7d0c95befd..489234a827 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt @@ -36,22 +36,24 @@ class DefaultHomeEntryPointTest { directLogoutView = { _ -> lambdaError() }, reportRoomEntryPoint = { _, _, _ -> lambdaError() }, declineInviteAndBlockUserEntryPoint = { _, _, _ -> lambdaError() }, - changeRoomMemberRolesEntryPoint = { _, _ -> lambdaError() }, + changeRoomMemberRolesEntryPoint = { _, _, _, _ -> lambdaError() }, leaveRoomRenderer = { _, _, _ -> lambdaError() }, ) } val callback = object : HomeEntryPoint.Callback { - override fun onRoomClick(roomId: RoomId) = lambdaError() - override fun onStartChatClick() = lambdaError() - override fun onSettingsClick() = lambdaError() - override fun onSetUpRecoveryClick() = lambdaError() - override fun onSessionConfirmRecoveryKeyClick() = lambdaError() - override fun onRoomSettingsClick(roomId: RoomId) = lambdaError() - override fun onReportBugClick() = lambdaError() + override fun navigateToRoom(roomId: RoomId) = lambdaError() + override fun navigateToCreateRoom() = lambdaError() + override fun navigateToSettings() = lambdaError() + override fun navigateToSetUpRecovery() = lambdaError() + override fun navigateToEnterRecoveryKey() = lambdaError() + override fun navigateToRoomSettings(roomId: RoomId) = lambdaError() + override fun navigateToBugReport() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(HomeFlowNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/declineandblock/DeclineInviteAndBlockEntryPoint.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/declineandblock/DeclineInviteAndBlockEntryPoint.kt index 8517fe2786..1bd4b0031b 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/declineandblock/DeclineInviteAndBlockEntryPoint.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/declineandblock/DeclineInviteAndBlockEntryPoint.kt @@ -13,5 +13,9 @@ import io.element.android.features.invite.api.InviteData import io.element.android.libraries.architecture.FeatureEntryPoint fun interface DeclineInviteAndBlockEntryPoint : FeatureEntryPoint { - fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData): Node + fun createNode( + parentNode: Node, + buildContext: BuildContext, + inviteData: InviteData, + ): Node } diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DefaultDeclineAndBlockEntryPoint.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DefaultDeclineAndBlockEntryPoint.kt index e76d173a90..00f91356c4 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DefaultDeclineAndBlockEntryPoint.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/declineandblock/DefaultDeclineAndBlockEntryPoint.kt @@ -17,7 +17,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultDeclineAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData): Node { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inviteData: InviteData, + ): Node { val inputs = DeclineAndBlockNode.Inputs(inviteData) return parentNode.createNode(buildContext, plugins = listOf(inputs)) } diff --git a/features/invite/test/build.gradle.kts b/features/invite/test/build.gradle.kts index e504873c3b..5669204b69 100644 --- a/features/invite/test/build.gradle.kts +++ b/features/invite/test/build.gradle.kts @@ -26,5 +26,6 @@ dependencies { implementation(libs.coroutines.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.test) + implementation(projects.tests.testutils) api(projects.features.invite.api) } diff --git a/features/invite/test/src/main/kotlin/io/element/android/features/invite/test/declineandblock/FakeDeclineInviteAndBlockEntryPoint.kt b/features/invite/test/src/main/kotlin/io/element/android/features/invite/test/declineandblock/FakeDeclineInviteAndBlockEntryPoint.kt new file mode 100644 index 0000000000..5df4ff6c0f --- /dev/null +++ b/features/invite/test/src/main/kotlin/io/element/android/features/invite/test/declineandblock/FakeDeclineInviteAndBlockEntryPoint.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.invite.test.declineandblock + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.invite.api.InviteData +import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeDeclineInviteAndBlockEntryPoint : DeclineInviteAndBlockEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inviteData: InviteData, + ): Node { + lambdaError() + } +} diff --git a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt index 9af4703949..0860c1053b 100644 --- a/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt +++ b/features/joinroom/api/src/main/kotlin/io/element/android/features/joinroom/api/JoinRoomEntryPoint.kt @@ -18,7 +18,11 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import java.util.Optional interface JoinRoomEntryPoint : FeatureEntryPoint { - fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node + fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: Inputs, + ): Node data class Inputs( val roomId: RoomId, diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPoint.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPoint.kt index c7e5e1d11f..0289dc5993 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPoint.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPoint.kt @@ -16,7 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultJoinRoomEntryPoint : JoinRoomEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: JoinRoomEntryPoint.Inputs): Node { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: JoinRoomEntryPoint.Inputs, + ): Node { return parentNode.createNode( buildContext = buildContext, plugins = listOf(inputs) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt index f501752544..aa1c0d452f 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomFlowNode.kt @@ -64,7 +64,11 @@ class JoinRoomFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { - is NavTarget.DeclineInviteAndBlockUser -> declineAndBlockEntryPoint.createNode(this, buildContext, navTarget.inviteData) + is NavTarget.DeclineInviteAndBlockUser -> declineAndBlockEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inviteData = navTarget.inviteData, + ) NavTarget.Root -> rootNode(buildContext) } } diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPointTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPointTest.kt index af75fd528c..570323c377 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPointTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/DefaultJoinRoomEntryPointTest.kt @@ -9,12 +9,10 @@ package io.element.android.features.joinroom.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom -import io.element.android.features.invite.api.InviteData -import io.element.android.features.invite.api.declineandblock.DeclineInviteAndBlockEntryPoint +import io.element.android.features.invite.test.declineandblock.FakeDeclineInviteAndBlockEntryPoint import io.element.android.features.joinroom.api.JoinRoomEntryPoint import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -40,9 +38,7 @@ class DefaultJoinRoomEntryPointTest { plugins = plugins, presenterFactory = { _, _, _, _, _ -> createJoinRoomPresenter() }, acceptDeclineInviteView = { _, _, _, _ -> lambdaError() }, - declineAndBlockEntryPoint = object : DeclineInviteAndBlockEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inviteData: InviteData) = lambdaError() - } + declineAndBlockEntryPoint = FakeDeclineInviteAndBlockEntryPoint(), ) } val inputs = JoinRoomEntryPoint.Inputs( @@ -52,7 +48,11 @@ class DefaultJoinRoomEntryPointTest { serverNames = emptyList(), trigger = JoinedRoom.Trigger.RoomDirectory, ) - val result = entryPoint.createNode(parentNode, BuildContext.root(null), inputs) + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + inputs = inputs, + ) assertThat(result).isInstanceOf(JoinRoomFlowNode::class.java) assertThat(result.plugins).contains(inputs) } diff --git a/features/knockrequests/test/build.gradle.kts b/features/knockrequests/test/build.gradle.kts new file mode 100644 index 0000000000..27928879b2 --- /dev/null +++ b/features/knockrequests/test/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.knockrequests.test" +} + +dependencies { + implementation(projects.features.knockrequests.api) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.tests.testutils) +} diff --git a/features/knockrequests/test/src/main/kotlin/io/element/android/features/knockrequests/test/FakeKnockRequestsListEntryPoint.kt b/features/knockrequests/test/src/main/kotlin/io/element/android/features/knockrequests/test/FakeKnockRequestsListEntryPoint.kt new file mode 100644 index 0000000000..bb5ab5045f --- /dev/null +++ b/features/knockrequests/test/src/main/kotlin/io/element/android/features/knockrequests/test/FakeKnockRequestsListEntryPoint.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.knockrequests.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeKnockRequestsListEntryPoint : KnockRequestsListEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + ): Node = lambdaError() +} diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt index 30e094e38e..ab16092065 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/DependenciesFlowNode.kt @@ -52,7 +52,7 @@ class DependenciesFlowNode( return when (navTarget) { is NavTarget.LicensesList -> { val callback = object : DependencyLicensesListNode.Callback { - override fun onOpenLicense(license: DependencyLicenseItem) { + override fun navigateToLicense(license: DependencyLicenseItem) { backstack.push(NavTarget.LicenseDetails(license)) } } diff --git a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt index 7280c2ad41..00493a0489 100644 --- a/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt +++ b/features/licenses/impl/src/main/kotlin/io/element/android/features/licenses/impl/list/DependencyLicensesListNode.kt @@ -12,12 +12,12 @@ 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.licenses.impl.model.DependencyLicenseItem +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -30,13 +30,10 @@ class DependencyLicensesListNode( plugins = plugins ) { interface Callback : Plugin { - fun onOpenLicense(license: DependencyLicenseItem) + fun navigateToLicense(license: DependencyLicenseItem) } - private fun onOpenLicense(license: DependencyLicenseItem) { - plugins() - .forEach { it.onOpenLicense(license) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -44,7 +41,7 @@ class DependencyLicensesListNode( DependencyLicensesListView( state = state, onBackClick = ::navigateUp, - onOpenLicense = ::onOpenLicense, + onOpenLicense = callback::navigateToLicense, ) } } diff --git a/features/licenses/test/build.gradle.kts b/features/licenses/test/build.gradle.kts new file mode 100644 index 0000000000..7ac79a9608 --- /dev/null +++ b/features/licenses/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.licenses.test" +} + +dependencies { + implementation(projects.features.licenses.api) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) +} diff --git a/features/licenses/test/src/main/kotlin/io/element/android/features/licenses/test/FakeOpenSourceLicensesEntryPoint.kt b/features/licenses/test/src/main/kotlin/io/element/android/features/licenses/test/FakeOpenSourceLicensesEntryPoint.kt new file mode 100644 index 0000000000..49772dd946 --- /dev/null +++ b/features/licenses/test/src/main/kotlin/io/element/android/features/licenses/test/FakeOpenSourceLicensesEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.licenses.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.licenses.api.OpenSourceLicensesEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeOpenSourceLicensesEntryPoint : OpenSourceLicensesEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + ): Node { + lambdaError() + } +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/SendLocationEntryPoint.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/SendLocationEntryPoint.kt index 1528e6cb14..afd1b895b5 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/SendLocationEntryPoint.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/SendLocationEntryPoint.kt @@ -18,8 +18,9 @@ import io.element.android.libraries.matrix.api.timeline.Timeline * Allows a user to share a location message within a room. */ interface SendLocationEntryPoint : FeatureEntryPoint { - fun builder(timelineMode: Timeline.Mode): Builder - interface Builder { - fun build(parentNode: Node, buildContext: BuildContext): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + timelineMode: Timeline.Mode, + ): Node } diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt index 7ae28a1e92..3c1f3ec288 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/ShowLocationEntryPoint.kt @@ -13,7 +13,14 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.architecture.NodeInputs interface ShowLocationEntryPoint : FeatureEntryPoint { - data class Inputs(val location: Location, val description: String?) : NodeInputs + data class Inputs( + val location: Location, + val description: String?, + ) : NodeInputs - fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs): Node + fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: Inputs, + ): Node } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPoint.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPoint.kt index 806fc5b741..2a8e9309a1 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPoint.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPoint.kt @@ -17,16 +17,14 @@ import io.element.android.libraries.matrix.api.timeline.Timeline @ContributesBinding(AppScope::class) class DefaultSendLocationEntryPoint : SendLocationEntryPoint { - override fun builder(timelineMode: Timeline.Mode): SendLocationEntryPoint.Builder { - return Builder(timelineMode) - } - - class Builder(private val timelineMode: Timeline.Mode) : SendLocationEntryPoint.Builder { - override fun build(parentNode: Node, buildContext: BuildContext): Node { - return parentNode.createNode( - buildContext = buildContext, - plugins = listOf(SendLocationNode.Inputs(timelineMode)) - ) - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + timelineMode: Timeline.Mode, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(SendLocationNode.Inputs(timelineMode)) + ) } } diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPoint.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPoint.kt index 75a177d21a..674dea4782 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPoint.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPoint.kt @@ -16,7 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultShowLocationEntryPoint : ShowLocationEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ShowLocationEntryPoint.Inputs): Node { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: ShowLocationEntryPoint.Inputs, + ): Node { return parentNode.createNode(buildContext, listOf(inputs)) } } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPointTest.kt index 9be79c7092..4cac3df6a2 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/DefaultSendLocationEntryPointTest.kt @@ -47,8 +47,11 @@ class DefaultSendLocationEntryPointTest { ) } val timelineMode = Timeline.Mode.Live - val result = entryPoint.builder(timelineMode) - .build(parentNode, BuildContext.root(null)) + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + timelineMode = timelineMode, + ) assertThat(result).isInstanceOf(SendLocationNode::class.java) assertThat(result.plugins).contains(SendLocationNode.Inputs(timelineMode)) } diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt index d31ef0c0e4..8d0d862c9b 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/show/DefaultShowLocationEntryPointTest.kt @@ -48,8 +48,8 @@ class DefaultShowLocationEntryPointTest { description = "My location", ) val result = entryPoint.createNode( - parentNode, - BuildContext.root(null), + parentNode = parentNode, + buildContext = BuildContext.root(null), inputs = inputs, ) assertThat(result).isInstanceOf(ShowLocationNode::class.java) diff --git a/features/location/test/build.gradle.kts b/features/location/test/build.gradle.kts index 024ae2c303..2171a0e0f5 100644 --- a/features/location/test/build.gradle.kts +++ b/features/location/test/build.gradle.kts @@ -14,5 +14,8 @@ android { } dependencies { - implementation(projects.features.location.api) + api(projects.features.location.api) + implementation(projects.libraries.matrix.api) + implementation(libs.appyx.core) + implementation(projects.tests.testutils) } diff --git a/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeSendLocationEntryPoint.kt b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeSendLocationEntryPoint.kt new file mode 100644 index 0000000000..ba39be70b1 --- /dev/null +++ b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeSendLocationEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.location.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.location.api.SendLocationEntryPoint +import io.element.android.libraries.matrix.api.timeline.Timeline +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeSendLocationEntryPoint : SendLocationEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + timelineMode: Timeline.Mode, + ): Node = lambdaError() +} diff --git a/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeShowLocationEntryPoint.kt b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeShowLocationEntryPoint.kt new file mode 100644 index 0000000000..a1ff742497 --- /dev/null +++ b/features/location/test/src/main/kotlin/io/element/android/features/location/test/FakeShowLocationEntryPoint.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.location.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.location.api.ShowLocationEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeShowLocationEntryPoint : ShowLocationEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: ShowLocationEntryPoint.Inputs, + ): Node = lambdaError() +} diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt index e9ba05ba74..c777d3a6b5 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenEntryPoint.kt @@ -15,13 +15,14 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface LockScreenEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: Target): NodeBuilder - fun pinUnlockIntent(context: Context): Intent + fun createNode( + parentNode: Node, + buildContext: BuildContext, + navTarget: Target, + callback: Callback, + ): Node - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun pinUnlockIntent(context: Context): Intent interface Callback : Plugin { fun onSetupDone() diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt index 0e22131e4c..39430c5ad1 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPoint.kt @@ -19,26 +19,24 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultLockScreenEntryPoint : LockScreenEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: LockScreenEntryPoint.Target): LockScreenEntryPoint.NodeBuilder { - val callbacks = mutableListOf() - - return object : LockScreenEntryPoint.NodeBuilder { - override fun callback(callback: LockScreenEntryPoint.Callback): LockScreenEntryPoint.NodeBuilder { - callbacks += callback - return this - } - - override fun build(): Node { - val inputs = LockScreenFlowNode.Inputs( + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + navTarget: LockScreenEntryPoint.Target, + callback: LockScreenEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + LockScreenFlowNode.Inputs( when (navTarget) { LockScreenEntryPoint.Target.Setup -> LockScreenFlowNode.NavTarget.Setup LockScreenEntryPoint.Target.Settings -> LockScreenFlowNode.NavTarget.Settings } - ) - val plugins = listOf(inputs) + callbacks - return parentNode.createNode(buildContext, plugins) - } - } + ), + callback, + ) + ) } override fun pinUnlockIntent(context: Context): Intent { diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt index 01aeaabe89..1597b584bc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsFlowNode.kt @@ -110,7 +110,7 @@ class LockScreenSettingsFlowNode( } NavTarget.Settings -> { val callback = object : LockScreenSettingsNode.Callback { - override fun onChangePinClick() { + override fun navigateToSetupPin() { backstack.push(NavTarget.SetupPin) } } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt index 5e27b815bc..d7a87e4ff4 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/settings/LockScreenSettingsNode.kt @@ -12,10 +12,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -26,12 +26,10 @@ class LockScreenSettingsNode( private val presenter: LockScreenSettingsPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onChangePinClick() + fun navigateToSetupPin() } - private fun onChangePinClick() { - plugins().forEach { it.onChangePinClick() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -39,7 +37,7 @@ class LockScreenSettingsNode( LockScreenSettingsView( state = state, onBackClick = this::navigateUp, - onChangePinClick = this::onChangePinClick, + onChangePinClick = callback::navigateToSetupPin, modifier = modifier, ) } diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt index 263cc8ea54..92ffd94a87 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/LockScreenSetupFlowNode.kt @@ -14,7 +14,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.newRoot import dev.zacsweers.metro.Assisted @@ -27,6 +26,7 @@ import io.element.android.features.lockscreen.impl.setup.biometric.SetupBiometri import io.element.android.features.lockscreen.impl.setup.pin.SetupPinNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -50,9 +50,7 @@ class LockScreenSetupFlowNode( fun onSetupDone() } - private fun onSetupDone() { - plugins().forEach { it.onSetupDone() } - } + private val callback: Callback = callback() sealed interface NavTarget : Parcelable { @Parcelize @@ -67,7 +65,7 @@ class LockScreenSetupFlowNode( if (biometricAuthenticatorManager.hasAvailableAuthenticator) { backstack.newRoot(NavTarget.Biometric) } else { - onSetupDone() + callback.onSetupDone() } } } @@ -91,7 +89,7 @@ class LockScreenSetupFlowNode( NavTarget.Biometric -> { val callback = object : SetupBiometricNode.Callback { override fun onBiometricSetupDone() { - onSetupDone() + callback.onSetupDone() } } createNode(buildContext, plugins = listOf(callback)) diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt index 64da1a321c..9ffd52e4bc 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/setup/biometric/SetupBiometricNode.kt @@ -13,10 +13,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -30,16 +30,14 @@ class SetupBiometricNode( fun onBiometricSetupDone() } - private fun onSetupDone() { - plugins().forEach { it.onBiometricSetupDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LaunchedEffect(state.isBiometricSetupDone) { if (state.isBiometricSetupDone) { - onSetupDone() + callback.onBiometricSetupDone() } } SetupBiometricView( diff --git a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt index fba460f6ee..b1d45c348c 100644 --- a/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt +++ b/features/lockscreen/impl/src/main/kotlin/io/element/android/features/lockscreen/impl/unlock/PinUnlockNode.kt @@ -13,10 +13,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -30,18 +30,14 @@ class PinUnlockNode( fun onUnlock() } - private fun onUnlock() { - plugins().forEach { - it.onUnlock() - } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LaunchedEffect(state.isUnlocked) { if (state.isUnlocked) { - onUnlock() + callback.onUnlock() } } PinUnlockView( diff --git a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPointTest.kt b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPointTest.kt index 822d275063..15473fe43c 100644 --- a/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPointTest.kt +++ b/features/lockscreen/impl/src/test/kotlin/io/element/android/features/lockscreen/impl/DefaultLockScreenEntryPointTest.kt @@ -37,9 +37,12 @@ class DefaultLockScreenEntryPointTest { override fun onSetupDone() = lambdaError() } val navTarget = LockScreenEntryPoint.Target.Setup - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + navTarget = navTarget, + callback = callback, + ) assertThat(result).isInstanceOf(LockScreenFlowNode::class.java) assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Setup)) assertThat(result.plugins).contains(callback) @@ -58,9 +61,12 @@ class DefaultLockScreenEntryPointTest { override fun onSetupDone() = lambdaError() } val navTarget = LockScreenEntryPoint.Target.Settings - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null), navTarget) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + navTarget = navTarget, + callback = callback, + ) assertThat(result).isInstanceOf(LockScreenFlowNode::class.java) assertThat(result.plugins).contains(LockScreenFlowNode.Inputs(LockScreenFlowNode.NavTarget.Settings)) assertThat(result.plugins).contains(callback) diff --git a/features/lockscreen/test/build.gradle.kts b/features/lockscreen/test/build.gradle.kts index 80f46e82c6..5d64c014b9 100644 --- a/features/lockscreen/test/build.gradle.kts +++ b/features/lockscreen/test/build.gradle.kts @@ -14,6 +14,8 @@ android { } dependencies { - implementation(libs.coroutines.core) api(projects.features.lockscreen.api) + implementation(libs.coroutines.core) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) } diff --git a/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenEntryPoint.kt b/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenEntryPoint.kt new file mode 100644 index 0000000000..1c41d9d6ea --- /dev/null +++ b/features/lockscreen/test/src/main/kotlin/io/element/android/features/lockscreen/test/FakeLockScreenEntryPoint.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.features.lockscreen.test + +import android.content.Context +import android.content.Intent +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.lockscreen.api.LockScreenEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeLockScreenEntryPoint : LockScreenEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + navTarget: LockScreenEntryPoint.Target, + callback: LockScreenEntryPoint.Callback, + ): Node = lambdaError() + + override fun pinUnlockIntent(context: Context): Intent = lambdaError() +} diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginEntryPoint.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginEntryPoint.kt index b3ef99ee19..bebe2f0afc 100644 --- a/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginEntryPoint.kt +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/LoginEntryPoint.kt @@ -19,14 +19,13 @@ interface LoginEntryPoint : FeatureEntryPoint { ) interface Callback : Plugin { - fun onReportProblem() + fun navigateToBugReport() } - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPoint.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPoint.kt index ed695f7b8e..71fbe1598c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPoint.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.login.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.login.api.LoginEntryPoint @@ -17,26 +16,21 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultLoginEntryPoint : LoginEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): LoginEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : LoginEntryPoint.NodeBuilder { - override fun params(params: LoginEntryPoint.Params): LoginEntryPoint.NodeBuilder { - plugins += LoginFlowNode.Params( + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: LoginEntryPoint.Params, + callback: LoginEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + LoginFlowNode.Params( accountProvider = params.accountProvider, loginHint = params.loginHint, - ) - return this - } - - override fun callback(callback: LoginEntryPoint.Callback): LoginEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + ), + callback, + ) + ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 4b83190f5d..208109d6f9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.singleTop @@ -41,6 +40,7 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -70,6 +70,7 @@ class LoginFlowNode( val loginHint: String?, ) : NodeInputs + private val callback: LoginEntryPoint.Callback = callback() private var activity: Activity? = null private var darkTheme: Boolean = false @@ -126,13 +127,13 @@ class LoginFlowNode( return when (navTarget) { NavTarget.OnBoarding -> { val callback = object : OnBoardingNode.Callback { - override fun onSignUp() { + override fun navigateToSignUpFlow() { backstack.push( NavTarget.ConfirmAccountProvider(isAccountCreation = true) ) } - override fun onSignIn(mustChooseAccountProvider: Boolean) { + override fun navigateToSignInFlow(mustChooseAccountProvider: Boolean) { backstack.push( if (mustChooseAccountProvider) { NavTarget.ChooseAccountProvider @@ -142,23 +143,23 @@ class LoginFlowNode( ) } - override fun onSignInWithQrCode() { + override fun navigateToQrCode() { backstack.push(NavTarget.QrCode) } - override fun onReportProblem() { - plugins().forEach { it.onReportProblem() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } - override fun onOidcDetails(oidcDetails: OidcDetails) { + override fun navigateToOidc(oidcDetails: OidcDetails) { navigateToMas(oidcDetails) } - override fun onCreateAccountContinue(url: String) { + override fun navigateToCreateAccount(url: String) { backstack.push(NavTarget.CreateAccount(url)) } - override fun onLoginPasswordNeeded() { + override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword) } } @@ -171,15 +172,15 @@ class LoginFlowNode( } NavTarget.ChooseAccountProvider -> { val callback = object : ChooseAccountProviderNode.Callback { - override fun onOidcDetails(oidcDetails: OidcDetails) { + override fun navigateToOidc(oidcDetails: OidcDetails) { navigateToMas(oidcDetails) } - override fun onCreateAccountContinue(url: String) { + override fun navigateToCreateAccount(url: String) { backstack.push(NavTarget.CreateAccount(url)) } - override fun onLoginPasswordNeeded() { + override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword) } } @@ -193,19 +194,19 @@ class LoginFlowNode( isAccountCreation = navTarget.isAccountCreation, ) val callback = object : ConfirmAccountProviderNode.Callback { - override fun onOidcDetails(oidcDetails: OidcDetails) { + override fun navigateToOidc(oidcDetails: OidcDetails) { navigateToMas(oidcDetails) } - override fun onCreateAccountContinue(url: String) { + override fun navigateToCreateAccount(url: String) { backstack.push(NavTarget.CreateAccount(url)) } - override fun onLoginPasswordNeeded() { + override fun navigateToLoginPassword() { backstack.push(NavTarget.LoginPassword) } - override fun onChangeAccountProvider() { + override fun navigateToChangeAccountProvider() { backstack.push(NavTarget.ChangeAccountProvider) } } @@ -221,7 +222,7 @@ class LoginFlowNode( backstack.singleTop(confirmAccountProvider) } - override fun onOtherClick() { + override fun navigateToSearchAccountProvider() { backstack.push(NavTarget.SearchAccountProvider) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt index 0fcef6bedf..2d9c2d2e9c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProvider.kt @@ -13,5 +13,4 @@ data class AccountProvider( val subtitle: String? = null, val isPublic: Boolean = false, val isMatrixOrg: Boolean = false, - val isValid: Boolean = false, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt index a5f0fd7d3b..886f0efb82 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderProvider.kt @@ -15,7 +15,7 @@ open class AccountProviderProvider : PreviewParameterProvider { get() = sequenceOf( anAccountProvider(), anAccountProvider().copy(subtitle = null), - anAccountProvider().copy(subtitle = null, title = "invalid", isValid = false), + anAccountProvider().copy(subtitle = null, title = "invalid"), anAccountProvider().copy(subtitle = null, title = "Other", isPublic = false, isMatrixOrg = false), // Add other state here ) @@ -26,11 +26,9 @@ fun anAccountProvider( subtitle: String? = "Matrix.org is an open network for secure, decentralized communication.", isPublic: Boolean = true, isMatrixOrg: Boolean = true, - isValid: Boolean = true, ) = AccountProvider( url = url, subtitle = subtitle, isPublic = isPublic, isMatrixOrg = isMatrixOrg, - isValid = isValid, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt index 22dcb9da9c..992bf18590 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/qrcode/QrCodeLoginFlowNode.kt @@ -147,11 +147,11 @@ class QrCodeLoginFlowNode( return when (navTarget) { is NavTarget.Initial -> { val callback = object : QrCodeIntroNode.Callback { - override fun onCancelClicked() { + override fun cancel() { navigateUp() } - override fun onContinue() { + override fun navigateToQrCodeScan() { backstack.push(NavTarget.QrCodeScan) } } @@ -159,11 +159,11 @@ class QrCodeLoginFlowNode( } is NavTarget.QrCodeScan -> { val callback = object : QrCodeScanNode.Callback { - override fun onScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) { + override fun handleScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) { lifecycleScope.startAuthentication(qrCodeLoginData) } - override fun onCancelClicked() { + override fun cancel() { backstack.pop() } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt index 0b4b088938..1e219a8a3b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverData.kt @@ -10,6 +10,4 @@ package io.element.android.features.login.impl.resolver data class HomeserverData( // The computed homeserver url, for which a wellknown file has been retrieved, or just a valid Url val homeserverUrl: String, - // True if a wellknown file has been found and is valid. If false, it means that the [homeserverUrl] is valid - val isWellknownValid: Boolean, ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt index 7b6f3e4102..c43839517c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/resolver/HomeserverResolver.kt @@ -8,19 +8,16 @@ package io.element.android.features.login.impl.resolver import dev.zacsweers.metro.Inject -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.parallelMap -import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.core.uri.isValidUrl -import io.element.android.libraries.wellknown.api.WellKnown -import io.element.android.libraries.wellknown.api.WellknownRetriever +import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout +import timber.log.Timber import java.util.Collections /** @@ -29,7 +26,7 @@ import java.util.Collections @Inject class HomeserverResolver( private val dispatchers: CoroutineDispatchers, - private val wellknownRetriever: WellknownRetriever, + private val homeServerLoginCompatibilityChecker: HomeServerLoginCompatibilityChecker, ) { fun resolve(userInput: String): Flow> = flow { val flowContext = currentCoroutineContext() @@ -41,20 +38,14 @@ class HomeserverResolver( // Run all the requests in parallel withContext(dispatchers.io) { list.parallelMap { url -> - val wellKnown = tryOrNull { - withTimeout(5000) { - wellknownRetriever.getWellKnown(url) - } - } - val isValid = wellKnown?.dataOrNull()?.isValid().orFalse() + val isValid = homeServerLoginCompatibilityChecker.check(url) + .onFailure { Timber.w(it, "Failed to check compatibility with homeserver $url") } + .getOrNull() + ?: return@parallelMap + + // Emit the list as soon as possible if (isValid) { - // Emit the list as soon as possible - currentList.add( - HomeserverData( - homeserverUrl = url, - isWellknownValid = true, - ) - ) + currentList.add(HomeserverData(homeserverUrl = url)) withContext(flowContext) { emit(currentList.toList()) } @@ -63,14 +54,7 @@ class HomeserverResolver( } // If list is empty, and the user has entered an URL, do not block the user. if (currentList.isEmpty() && trimmedUserInput.isValidUrl()) { - emit( - listOf( - HomeserverData( - homeserverUrl = trimmedUserInput, - isWellknownValid = false, - ) - ) - ) + emit(listOf(HomeserverData(homeserverUrl = trimmedUserInput))) } } @@ -88,7 +72,3 @@ class HomeserverResolver( } } } - -private fun WellKnown.isValid(): Boolean { - return homeServer?.baseURL?.isNotBlank().orFalse() -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt index a0587211c0..b63b7cf011 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -29,16 +29,10 @@ class ChangeAccountProviderNode( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun onDone() - fun onOtherClick() + fun navigateToSearchAccountProvider() } - private fun onDone() { - plugins().forEach { it.onDone() } - } - - private fun onOtherClick() { - plugins().forEach { it.onOtherClick() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -49,8 +43,8 @@ class ChangeAccountProviderNode( modifier = modifier, onBackClick = ::navigateUp, onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = ::onDone, - onOtherProviderClick = ::onOtherClick, + onSuccess = callback::onDone, + onOtherProviderClick = callback::navigateToSearchAccountProvider, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt index 8e6a3ef0ba..940889728e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenter.kt @@ -37,7 +37,6 @@ class ChangeAccountProviderPresenter( subtitle = null, isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, - isValid = true, ) } .toImmutableList() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt index 128d235c93..2b65c2a70f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) @@ -29,22 +29,12 @@ class ChooseAccountProviderNode( private val presenter: ChooseAccountProviderPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onLoginPasswordNeeded() - fun onOidcDetails(oidcDetails: OidcDetails) - fun onCreateAccountContinue(url: String) + fun navigateToLoginPassword() + fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToCreateAccount(url: String) } - private fun onOidcDetails(oidcDetails: OidcDetails) { - plugins().forEach { it.onOidcDetails(oidcDetails) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.onLoginPasswordNeeded() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.onCreateAccountContinue(url) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -54,10 +44,10 @@ class ChooseAccountProviderNode( state = state, modifier = modifier, onBackClick = ::navigateUp, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = ::onCreateAccountContinue, + onCreateAccountContinue = callback::navigateToCreateAccount, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt index 73f03ba7c8..0c915959cb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenter.kt @@ -67,7 +67,6 @@ class ChooseAccountProviderPresenter( subtitle = null, isPublic = url == AuthenticationConfig.MATRIX_ORG_URL, isMatrixOrg = url == AuthenticationConfig.MATRIX_ORG_URL, - isValid = true, ) } .toImmutableList() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt index 0d50d21a18..e849d0d404 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/confirmaccountprovider/ConfirmAccountProviderNode.kt @@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -42,27 +42,13 @@ class ConfirmAccountProviderNode( ) interface Callback : Plugin { - fun onLoginPasswordNeeded() - fun onOidcDetails(oidcDetails: OidcDetails) - fun onCreateAccountContinue(url: String) - fun onChangeAccountProvider() + fun navigateToLoginPassword() + fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToCreateAccount(url: String) + fun navigateToChangeAccountProvider() } - private fun onOidcDetails(data: OidcDetails) { - plugins().forEach { it.onOidcDetails(data) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.onLoginPasswordNeeded() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.onCreateAccountContinue(url) } - } - - private fun onChangeAccountProvider() { - plugins().forEach { it.onChangeAccountProvider() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -71,10 +57,10 @@ class ConfirmAccountProviderNode( ConfirmAccountProviderView( state = state, modifier = modifier, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, - onCreateAccountContinue = ::onCreateAccountContinue, - onChange = ::onChangeAccountProvider, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, + onCreateAccountContinue = callback::navigateToCreateAccount, + onChange = callback::navigateToChangeAccountProvider, onLearnMoreClick = { openLearnMorePage(context) }, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt index 3652a3df8d..f90483778a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingNode.kt @@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.matrix.api.auth.OidcDetails @@ -34,13 +34,13 @@ class OnBoardingNode( plugins = plugins ) { interface Callback : Plugin { - fun onSignUp() - fun onSignIn(mustChooseAccountProvider: Boolean) - fun onSignInWithQrCode() - fun onReportProblem() - fun onLoginPasswordNeeded() - fun onOidcDetails(oidcDetails: OidcDetails) - fun onCreateAccountContinue(url: String) + fun navigateToSignUpFlow() + fun navigateToSignInFlow(mustChooseAccountProvider: Boolean) + fun navigateToQrCode() + fun navigateToBugReport() + fun navigateToLoginPassword() + fun navigateToOidc(oidcDetails: OidcDetails) + fun navigateToCreateAccount(url: String) } data class Params( @@ -48,40 +48,13 @@ class OnBoardingNode( val loginHint: String?, ) : NodeInputs + private val callback: Callback = callback() private val params = inputs() private val presenter = presenterFactory.create( params = params, ) - private fun onSignIn(mustChooseAccountProvider: Boolean) { - plugins().forEach { it.onSignIn(mustChooseAccountProvider) } - } - - private fun onSignUp() { - plugins().forEach { it.onSignUp() } - } - - private fun onSignInWithQrCode() { - plugins().forEach { it.onSignInWithQrCode() } - } - - private fun onReportProblem() { - plugins().forEach { it.onReportProblem() } - } - - private fun onOidcDetails(data: OidcDetails) { - plugins().forEach { it.onOidcDetails(data) } - } - - private fun onLoginPasswordNeeded() { - plugins().forEach { it.onLoginPasswordNeeded() } - } - - private fun onCreateAccountContinue(url: String) { - plugins().forEach { it.onCreateAccountContinue(url) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -89,14 +62,14 @@ class OnBoardingNode( OnBoardingView( state = state, modifier = modifier, - onSignIn = ::onSignIn, - onCreateAccount = ::onSignUp, - onSignInWithQrCode = ::onSignInWithQrCode, - onReportProblem = ::onReportProblem, - onOidcDetails = ::onOidcDetails, - onNeedLoginPassword = ::onLoginPasswordNeeded, + onSignIn = callback::navigateToSignInFlow, + onCreateAccount = callback::navigateToSignUpFlow, + onSignInWithQrCode = callback::navigateToQrCode, + onReportProblem = callback::navigateToBugReport, + onOidcDetails = callback::navigateToOidc, + onNeedLoginPassword = callback::navigateToLoginPassword, onLearnMoreClick = { openLearnMorePage(context) }, - onCreateAccountContinue = ::onCreateAccountContinue, + onCreateAccountContinue = callback::navigateToCreateAccount, onBackClick = ::navigateUp, ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt index e7e20aa70d..44d8095fa3 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/onboarding/OnBoardingPresenter.kt @@ -90,7 +90,7 @@ class OnBoardingPresenter( } val isAddingAccount by produceState(initialValue = false) { // We are adding an account if there is at least one session already stored - value = sessionStore.getAllSessions().isNotEmpty() + value = sessionStore.numberOfSessions() > 0 } val loginMode by loginHelper.collectLoginMode() diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt index 8b8d5b2dd5..7a3689dd3c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/confirmation/QrCodeConfirmationNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(QrCodeLoginScope::class) @@ -29,17 +29,14 @@ class QrCodeConfirmationNode( fun onCancel() } + private val callback: Callback = callback() private val step = inputs() - private fun onCancel() { - plugins().forEach { it.onCancel() } - } - @Composable override fun View(modifier: Modifier) { QrCodeConfirmationView( step = step, - onCancel = ::onCancel, + onCancel = callback::onCancel, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt index 7b46c2e45c..294037a1f7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/error/QrCodeErrorNode.kt @@ -12,12 +12,12 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope import io.element.android.features.login.impl.qrcode.QrCodeErrorScreenType +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.core.meta.BuildMeta @@ -32,10 +32,7 @@ class QrCodeErrorNode( fun onRetry() } - private fun onRetry() { - plugins().forEach { it.onRetry() } - } - + private val callback: Callback = callback() private val qrCodeErrorScreenType = inputs() @Composable @@ -44,7 +41,7 @@ class QrCodeErrorNode( modifier = modifier, errorScreenType = qrCodeErrorScreenType, appName = buildMeta.productionApplicationName, - onRetry = ::onRetry, + onRetry = callback::onRetry, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt index c86dc6096a..719df8f423 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/intro/QrCodeIntroNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback @ContributesNode(QrCodeLoginScope::class) @AssistedInject @@ -26,25 +26,19 @@ class QrCodeIntroNode( private val presenter: QrCodeIntroPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onCancelClicked() - fun onContinue() + fun cancel() + fun navigateToQrCodeScan() } - private fun onCancelClicked() { - plugins().forEach { it.onCancelClicked() } - } - - private fun onContinue() { - plugins().forEach { it.onContinue() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() QrCodeIntroView( state = state, - onBackClick = ::onCancelClicked, - onContinue = ::onContinue, + onBackClick = callback::cancel, + onContinue = callback::navigateToQrCodeScan, modifier = modifier ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt index f6b52522b5..44eab816c1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/qrcode/scan/QrCodeScanNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.di.QrCodeLoginScope +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.auth.qrlogin.MatrixQrCodeLoginData @ContributesNode(QrCodeLoginScope::class) @@ -27,25 +27,19 @@ class QrCodeScanNode( private val presenter: QrCodeScanPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) - fun onCancelClicked() + fun handleScannedCode(qrCodeLoginData: MatrixQrCodeLoginData) + fun cancel() } - private fun onQrCodeDataReady(qrCodeLoginData: MatrixQrCodeLoginData) { - plugins().forEach { it.onScannedCode(qrCodeLoginData) } - } - - private fun onCancelClicked() { - plugins().forEach { it.onCancelClicked() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() QrCodeScanView( state = state, - onQrCodeDataReady = ::onQrCodeDataReady, - onBackClick = ::onCancelClicked, + onQrCodeDataReady = callback::handleScannedCode, + onBackClick = callback::cancel, modifier = modifier ) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt index dc6084032e..5def2bc830 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderNode.kt @@ -13,12 +13,12 @@ import androidx.compose.ui.platform.LocalContext 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.login.impl.util.openLearnMorePage +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -31,9 +31,7 @@ class SearchAccountProviderNode( fun onDone() } - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -44,7 +42,7 @@ class SearchAccountProviderNode( modifier = modifier, onBackClick = ::navigateUp, onLearnMoreClick = { openLearnMorePage(context) }, - onSuccess = ::onDone, + onSuccess = callback::onDone, ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt index 0aa06ca632..657a21111c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenter.kt @@ -57,14 +57,14 @@ class SearchAccountProviderPresenter( userInput = userInput, userInputResult = data.value, changeServerState = changeServerState, - eventSink = ::handleEvents + eventSink = ::handleEvents, ) } private fun CoroutineScope.onUserInput(userInput: String, data: MutableState>>) = launch { data.value = AsyncData.Uninitialized // Debounce - delay(300) + delay(500) data.value = AsyncData.Loading() homeserverResolver.resolve(userInput).collect { data.value = AsyncData.Success(it) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt index fb0e0f5c5a..3dd7a3d8c5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderStateProvider.kt @@ -34,18 +34,14 @@ fun aSearchAccountProviderState( fun aHomeserverDataList(): List { return listOf( - aHomeserverData(isWellknownValid = true), - aHomeserverData(homeserverUrl = "https://no.sliding.sync", isWellknownValid = true), - aHomeserverData(homeserverUrl = "https://invalid", isWellknownValid = false), + aHomeserverData(homeserverUrl = AuthenticationConfig.MATRIX_ORG_URL), + aHomeserverData(homeserverUrl = "https://no.sliding.sync"), + aHomeserverData(homeserverUrl = "https://invalid"), ) } fun aHomeserverData( homeserverUrl: String = AuthenticationConfig.MATRIX_ORG_URL, - isWellknownValid: Boolean = true, ): HomeserverData { - return HomeserverData( - homeserverUrl = homeserverUrl, - isWellknownValid = isWellknownValid, - ) + return HomeserverData(homeserverUrl = homeserverUrl,) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt index 13bfd9e38e..2b289aa4c1 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderView.kt @@ -192,7 +192,6 @@ private fun HomeserverData.toAccountProvider(): AccountProvider { // There is no need to know for other servers right now isPublic = isMatrixOrg, isMatrixOrg = isMatrixOrg, - isValid = isWellknownValid, ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt index c10d22c51a..4a6f6cae22 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/DefaultLoginEntryPointTest.kt @@ -39,16 +39,18 @@ class DefaultLoginEntryPointTest { ) } val callback = object : LoginEntryPoint.Callback { - override fun onReportProblem() = lambdaError() + override fun navigateToBugReport() = lambdaError() } val params = LoginEntryPoint.Params( accountProvider = "ac", loginHint = "lh", ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(LoginFlowNode::class.java) assertThat(result.plugins).contains(LoginFlowNode.Params(params.accountProvider, params.loginHint)) assertThat(result.plugins).contains(callback) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt index 3d84a1da41..8a8f6864cf 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/accountprovider/AccountProviderDataSourceTest.kt @@ -33,7 +33,6 @@ class AccountProviderDataSourceTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = false, ) ) } @@ -55,7 +54,6 @@ class AccountProviderDataSourceTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = false, ) ) } @@ -77,7 +75,6 @@ class AccountProviderDataSourceTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = false, ) ) } @@ -98,7 +95,6 @@ class AccountProviderDataSourceTest { subtitle = null, isPublic = false, isMatrixOrg = false, - isValid = false, ) ) sut.reset() diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt index 89abc6ddef..f2e933390b 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/changeaccountprovider/ChangeAccountProviderPresenterTest.kt @@ -46,7 +46,6 @@ class ChangeAccountProviderPresenterTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = true, ) ) ) @@ -76,7 +75,6 @@ class ChangeAccountProviderPresenterTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = true, ), AccountProvider( url = "https://element.io", @@ -84,7 +82,6 @@ class ChangeAccountProviderPresenterTest { subtitle = null, isPublic = false, isMatrixOrg = false, - isValid = true, ) ) ) @@ -114,7 +111,6 @@ class ChangeAccountProviderPresenterTest { subtitle = null, isPublic = true, isMatrixOrg = true, - isValid = true, ) ) ) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt index 5bad8a3638..2e13b0e555 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/chooseaccountprovider/ChooseAccountProviderPresenterTest.kt @@ -37,14 +37,12 @@ class ChooseAccountProviderPresenterTest { subtitle = null, isPublic = false, isMatrixOrg = false, - isValid = true, ) val accountProvider2 = AccountProvider( url = ACCOUNT_PROVIDER_FROM_CONFIG_2.ensureProtocol(), subtitle = null, isPublic = false, isMatrixOrg = false, - isValid = true, ) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt index 79cd3c954a..67453119c7 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/screens/searchaccountprovider/SearchAccountProviderPresenterTest.kt @@ -13,12 +13,8 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.login.impl.changeserver.aChangeServerState import io.element.android.features.login.impl.resolver.HomeserverResolver -import io.element.android.features.wellknown.test.FakeWellknownRetriever import io.element.android.libraries.architecture.AsyncData -import io.element.android.libraries.matrix.test.A_HOMESERVER_URL -import io.element.android.libraries.wellknown.api.WellKnown -import io.element.android.libraries.wellknown.api.WellKnownBaseConfig -import io.element.android.libraries.wellknown.api.WellknownRetrieverResult +import io.element.android.libraries.matrix.test.auth.FakeHomeServerLoginCompatibilityChecker import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -33,9 +29,9 @@ class SearchAccountProviderPresenterTest { @Test fun `present - initial state`() = runTest { - val fakeWellknownRetriever = FakeWellknownRetriever() + val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.success(true) }) val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), + homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) moleculeFlow(RecompositionMode.Immediate) { @@ -47,9 +43,35 @@ class SearchAccountProviderPresenterTest { } } + @Test + fun `present - error while checking login compatibility`() = runTest { + val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.failure(IllegalStateException("Oops")) }) + val presenter = SearchAccountProviderPresenter( + homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), + changeServerPresenter = { aChangeServerState() } + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org")) + val withInputState = awaitItem() + assertThat(withInputState.userInput).isEqualTo("https://test.org") + assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) + assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) + assertThat(awaitItem().userInputResult).isEqualTo( + AsyncData.Success( + listOf( + aHomeserverData(homeserverUrl = "https://test.org") + ) + ) + ) + } + } + @Test fun `present - enter text no result`() = runTest { - val fakeWellknownRetriever = FakeWellknownRetriever() + val fakeWellknownRetriever = FakeHomeServerLoginCompatibilityChecker(checkResult = { Result.success(false) }) val presenter = SearchAccountProviderPresenter( homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), changeServerPresenter = { aChangeServerState() } @@ -67,48 +89,20 @@ class SearchAccountProviderPresenterTest { } } - @Test - fun `present - enter valid url no wellknown`() = runTest { - val fakeWellknownRetriever = FakeWellknownRetriever() - val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), - changeServerPresenter = { aChangeServerState() } - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem() - initialState.eventSink.invoke(SearchAccountProviderEvents.UserInput("https://test.org")) - val withInputState = awaitItem() - assertThat(withInputState.userInput).isEqualTo("https://test.org") - assertThat(initialState.userInputResult).isEqualTo(AsyncData.Uninitialized) - assertThat(awaitItem().userInputResult).isInstanceOf(AsyncData.Loading::class.java) - assertThat(awaitItem().userInputResult).isEqualTo( - AsyncData.Success( - listOf( - aHomeserverData(homeserverUrl = "https://test.org", isWellknownValid = false) - ) - ) - ) - } - } - @Test fun `present - enter text one result with wellknown`() = runTest { - val getWellKnownResult = lambdaRecorder> { + val checkResult = lambdaRecorder> { when (it) { - "https://test.org" -> WellknownRetrieverResult.NotFound - "https://test.com" -> WellknownRetrieverResult.NotFound - "https://test.io" -> WellknownRetrieverResult.Success(aWellKnown()) - "https://test" -> WellknownRetrieverResult.NotFound + "https://test.org" -> Result.success(false) + "https://test.com" -> Result.success(false) + "https://test.io" -> Result.success(true) + "https://test" -> Result.success(false) else -> error("should not happen") } } - val fakeWellknownRetriever = FakeWellknownRetriever( - getWellKnownResult = getWellKnownResult, - ) + val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = checkResult) val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), + homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) moleculeFlow(RecompositionMode.Immediate) { @@ -127,7 +121,7 @@ class SearchAccountProviderPresenterTest { ) ) ) - getWellKnownResult.assertions().isCalledExactly(4) + checkResult.assertions().isCalledExactly(4) .withSequence( listOf(value("https://test.org")), listOf(value("https://test.com")), @@ -139,20 +133,18 @@ class SearchAccountProviderPresenterTest { @Test fun `present - enter text two results with wellknown`() = runTest { - val getWellKnownResult = lambdaRecorder> { + val checkResult = lambdaRecorder> { when (it) { - "https://test.org" -> WellknownRetrieverResult.Success(aWellKnown()) - "https://test.com" -> WellknownRetrieverResult.NotFound - "https://test.io" -> WellknownRetrieverResult.Success(aWellKnown()) - "https://test" -> WellknownRetrieverResult.NotFound + "https://test.org" -> Result.success(true) + "https://test.com" -> Result.success(false) + "https://test.io" -> Result.success(true) + "https://test" -> Result.success(false) else -> error("should not happen") } } - val fakeWellknownRetriever = FakeWellknownRetriever( - getWellKnownResult = getWellKnownResult, - ) + val fakeLoginCompatibilityChecker = FakeHomeServerLoginCompatibilityChecker(checkResult = checkResult) val presenter = SearchAccountProviderPresenter( - homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeWellknownRetriever), + homeserverResolver = HomeserverResolver(testCoroutineDispatchers(), fakeLoginCompatibilityChecker), changeServerPresenter = { aChangeServerState() } ) moleculeFlow(RecompositionMode.Immediate) { @@ -179,7 +171,7 @@ class SearchAccountProviderPresenterTest { ) ) ) - getWellKnownResult.assertions().isCalledExactly(4) + checkResult.assertions().isCalledExactly(4) .withSequence( listOf(value("https://test.org")), listOf(value("https://test.com")), @@ -188,15 +180,4 @@ class SearchAccountProviderPresenterTest { ) } } - - private fun aWellKnown(): WellKnown { - return WellKnown( - homeServer = WellKnownBaseConfig( - baseURL = A_HOMESERVER_URL - ), - identityServer = WellKnownBaseConfig( - baseURL = A_HOMESERVER_URL - ), - ) - } } diff --git a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutEntryPoint.kt b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutEntryPoint.kt index 17f67813ca..cf7deaf334 100644 --- a/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutEntryPoint.kt +++ b/features/logout/api/src/main/kotlin/io/element/android/features/logout/api/LogoutEntryPoint.kt @@ -13,14 +13,13 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface LogoutEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { - fun onChangeRecoveryKeyClick() + fun navigateToSecureBackup() } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPoint.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPoint.kt index adcc060b46..844c5ad0ad 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPoint.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.logout.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.logout.api.LogoutEntryPoint @@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultLogoutEntryPoint : LogoutEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): LogoutEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : LogoutEntryPoint.NodeBuilder { - override fun callback(callback: LogoutEntryPoint.Callback): LogoutEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: LogoutEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt index d554dbe8f0..7cf717df68 100644 --- a/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt +++ b/features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.logout.api.LogoutEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -26,16 +26,14 @@ class LogoutNode( @Assisted plugins: List, private val presenter: LogoutPresenter, ) : Node(buildContext, plugins = plugins) { - private fun onChangeRecoveryKeyClick() { - plugins().forEach { it.onChangeRecoveryKeyClick() } - } + private val callback: LogoutEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() LogoutView( state = state, - onChangeRecoveryKeyClick = ::onChangeRecoveryKeyClick, + onChangeRecoveryKeyClick = callback::navigateToSecureBackup, onBackClick = ::navigateUp, modifier = modifier, ) diff --git a/features/logout/impl/src/main/res/values-fa/translations.xml b/features/logout/impl/src/main/res/values-fa/translations.xml index 4bf6be1b89..c4c6e823d0 100644 --- a/features/logout/impl/src/main/res/values-fa/translations.xml +++ b/features/logout/impl/src/main/res/values-fa/translations.xml @@ -4,6 +4,7 @@ "خروج" "خروج" "خارج شدن…" + "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید پیام‌های رمزنگاشته‌تان را از دست خواهید داد." "پشتیبان را خاموش کرده‌اید" "کلیدهایتان هنوز در حال پشتیبان گیریند" "لطفاً پیش از خروج منتظر پایانش شوید." @@ -11,5 +12,6 @@ "خروج" "شما در آستانه خروج از آخرین جلسه خود هستید. اگر اکنون از سیستم خارج شوید، دسترسی به پیام های رمزگذاری شده تان را از دست خواهید داد." "بازگردانی برپا نشده" + "دارید از واپسین نشستتان خارج می‌شوید. اگر اکنون خارج شوید ممکن است پیام‌های رمزنگاشته‌تان را از دست بدهید." "کلید بازیابیتان را ذخیره کرده‌اید؟" diff --git a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPointTest.kt b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPointTest.kt index 01d1bfc6ca..dd4b3b36fc 100644 --- a/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPointTest.kt +++ b/features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultLogoutEntryPointTest.kt @@ -32,11 +32,13 @@ class DefaultLogoutEntryPointTest { ) } val callback = object : LogoutEntryPoint.Callback { - override fun onChangeRecoveryKeyClick() = lambdaError() + override fun navigateToSecureBackup() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(LogoutNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/features/logout/test/build.gradle.kts b/features/logout/test/build.gradle.kts index e7647a85db..ff1b6ae180 100644 --- a/features/logout/test/build.gradle.kts +++ b/features/logout/test/build.gradle.kts @@ -15,6 +15,7 @@ android { dependencies { implementation(libs.coroutines.core) + implementation(projects.libraries.architecture) implementation(projects.tests.testutils) api(projects.features.logout.api) } diff --git a/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutEntryPoint.kt b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutEntryPoint.kt new file mode 100644 index 0000000000..953fe87587 --- /dev/null +++ b/features/logout/test/src/main/kotlin/io/element/android/features/logout/test/FakeLogoutEntryPoint.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.logout.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.logout.api.LogoutEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeLogoutEntryPoint : LogoutEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: LogoutEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt index 1d689663ee..99f9ed79a0 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt @@ -31,25 +31,24 @@ interface MessagesEntryPoint : FeatureEntryPoint { data object PinnedMessages : InitialTarget } - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } - interface Callback : Plugin { - fun onRoomDetailsClick() - fun onUserDataClick(userId: UserId) - fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) - fun forwardEvent(eventId: EventId) - fun openRoom(roomId: RoomId) + fun navigateToRoomDetails() + fun navigateToRoomMemberDetails(userId: UserId) + fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) + fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) + fun navigateToRoom(roomId: RoomId) } data class Params(val initialTarget: InitialTarget) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder -} + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node -interface MessagesEntryPointNode { - suspend fun attachThread(threadId: ThreadId, focusedEventId: EventId?) + interface NodeProxy { + suspend fun attachThread(threadId: ThreadId, focusedEventId: EventId?) + } } diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.kt new file mode 100644 index 0000000000..026486e00a --- /dev/null +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/pinned/PinnedEventsTimelineProvider.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.messages.api.pinned + +import io.element.android.libraries.matrix.api.timeline.TimelineProvider + +interface PinnedEventsTimelineProvider : TimelineProvider diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index cd72c4d84a..e74072830d 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -79,6 +79,9 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.dateformatter.test) testImplementation(projects.libraries.push.test) + testImplementation(projects.features.call.test) + testImplementation(projects.features.forward.test) + testImplementation(projects.features.knockrequests.test) testImplementation(projects.features.location.test) testImplementation(projects.features.networkmonitor.test) testImplementation(projects.features.messages.test) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt index 9085bcb283..1b20580b62 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPoint.kt @@ -9,34 +9,20 @@ package io.element.android.features.messages.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.features.messages.api.MessagesEntryPoint -import io.element.android.libraries.architecture.NodeFactoriesBindings -import io.element.android.libraries.architecture.bindings +import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultMessagesEntryPoint : MessagesEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder { - val nodeFactories = parentNode.bindings().nodeFactories() - val plugins = ArrayList() - - return object : MessagesEntryPoint.NodeBuilder { - override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder { - plugins += MessagesEntryPoint.Params(params.initialTarget) - return this - } - - override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return nodeFactories[MessagesFlowNode::class]!!.create(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: MessagesEntryPoint.Params, + callback: MessagesEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(params, callback)) } } 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 73aad6f7de..d51ebd250e 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 @@ -16,7 +16,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -33,10 +32,9 @@ import io.element.android.features.location.api.LocationService import io.element.android.features.location.api.SendLocationEntryPoint import io.element.android.features.location.api.ShowLocationEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint -import io.element.android.features.messages.api.MessagesEntryPointNode import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode -import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider +import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.features.messages.impl.pinned.list.PinnedMessagesListNode import io.element.android.features.messages.impl.report.ReportMessageNode import io.element.android.features.messages.impl.threads.ThreadedMessagesNode @@ -55,6 +53,7 @@ import io.element.android.features.poll.api.create.CreatePollEntryPoint import io.element.android.features.poll.api.create.CreatePollMode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.hide @@ -115,7 +114,7 @@ class MessagesFlowNode( private val roomNamesCache: RoomNamesCache, private val mentionSpanUpdater: MentionSpanUpdater, private val mentionSpanTheme: MentionSpanTheme, - private val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider, + private val pinnedEventsTimelineProvider: DefaultPinnedEventsTimelineProvider, private val timelineController: TimelineController, private val knockRequestsListEntryPoint: KnockRequestsListEntryPoint, private val dateFormatter: DateFormatter, @@ -130,8 +129,7 @@ class MessagesFlowNode( ), buildContext = buildContext, plugins = plugins, -), - MessagesEntryPointNode { +), MessagesEntryPoint.NodeProxy { sealed interface NavTarget : Parcelable { @Parcelize data class Messages(val focusedEventId: EventId?) : NavTarget @@ -182,7 +180,7 @@ class MessagesFlowNode( data class Thread(val threadRootId: ThreadId, val focusedEventId: EventId?) : NavTarget } - private val callbacks = plugins() + private val callback: MessagesEntryPoint.Callback = callback() override fun onBuilt() { super.onBuilt() @@ -220,18 +218,18 @@ class MessagesFlowNode( return when (navTarget) { is NavTarget.Messages -> { val callback = object : MessagesNode.Callback { - override fun onRoomDetailsClick() { - callbacks.forEach { it.onRoomDetailsClick() } + override fun navigateToRoomDetails() { + callback.navigateToRoomDetails() } - override fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { + override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { return processEventClick( timelineMode = timelineMode, event = event, ) } - override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { + override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { backstack.push( NavTarget.AttachmentPreview( attachment = attachments.first(), @@ -241,39 +239,39 @@ class MessagesFlowNode( ) } - override fun onUserDataClick(userId: UserId) { - callbacks.forEach { it.onUserDataClick(userId) } + override fun navigateToRoomMemberDetails(userId: UserId) { + callback.navigateToRoomMemberDetails(userId) } - override fun onPermalinkClick(data: PermalinkData) { - callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = true) } + override fun handlePermalinkClick(data: PermalinkData) { + callback.handlePermalinkClick(data, pushToBackstack = true) } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } - override fun onForwardEventClick(eventId: EventId) { + override fun forwardEvent(eventId: EventId) { backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false)) } - override fun onReportMessage(eventId: EventId, senderId: UserId) { + override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { backstack.push(NavTarget.ReportMessage(eventId, senderId)) } - override fun onSendLocationClick() { + override fun navigateToSendLocation() { backstack.push(NavTarget.SendLocation(Timeline.Mode.Live)) } - override fun onCreatePollClick() { + override fun navigateToCreatePoll() { backstack.push(NavTarget.CreatePoll(Timeline.Mode.Live)) } - override fun onEditPollClick(eventId: EventId) { + override fun navigateToEditPoll(eventId: EventId) { backstack.push(NavTarget.EditPoll(Timeline.Mode.Live, eventId)) } - override fun onJoinCallClick(roomId: RoomId) { + override fun navigateToRoomCall(roomId: RoomId) { val callType = CallType.RoomCall( sessionId = sessionId, roomId = roomId, @@ -282,15 +280,15 @@ class MessagesFlowNode( elementCallEntryPoint.startCall(callType) } - override fun onViewAllPinnedEvents() { + override fun navigateToPinnedMessagesList() { backstack.push(NavTarget.PinnedMessagesList) } - override fun onViewKnockRequests() { + override fun navigateToKnockRequestsList() { backstack.push(NavTarget.KnockRequestsList) } - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { + override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { backstack.push(NavTarget.Thread(threadRootId, focusedEventId)) } } @@ -311,19 +309,21 @@ class MessagesFlowNode( overlay.hide() } - override fun onViewInTimeline(eventId: EventId) { - viewInTimeline(eventId) + override fun viewInTimeline(eventId: EventId) { + this@MessagesFlowNode.viewInTimeline(eventId) } - override fun onForwardEvent(eventId: EventId) { + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { // Need to go to the parent because of the overlay - forwardEvent(eventId) + callback.forwardEvent(eventId, fromPinnedEvents) } } - mediaViewerEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + mediaViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback + ) } is NavTarget.AttachmentPreview -> { val inputs = AttachmentsPreviewNode.Inputs( @@ -335,7 +335,11 @@ class MessagesFlowNode( } is NavTarget.LocationViewer -> { val inputs = ShowLocationEntryPoint.Inputs(navTarget.location, navTarget.description) - showLocationEntryPoint.createNode(this, buildContext, inputs) + showLocationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + inputs = inputs, + ) } is NavTarget.EventDebugInfo -> { val inputs = EventDebugInfoNode.Inputs(navTarget.eventId, navTarget.debugInfo) @@ -352,70 +356,74 @@ class MessagesFlowNode( override fun onDone(roomIds: List) { backstack.pop() roomIds.singleOrNull()?.let { roomId -> - callbacks.forEach { it.openRoom(roomId) } + callback.navigateToRoom(roomId) } } } - forwardEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + forwardEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } is NavTarget.ReportMessage -> { val inputs = ReportMessageNode.Inputs(navTarget.eventId, navTarget.senderId) createNode(buildContext, listOf(inputs)) } is NavTarget.SendLocation -> { - sendLocationEntryPoint - .builder(navTarget.timelineMode) - .build(this, buildContext) + sendLocationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + timelineMode = navTarget.timelineMode, + ) } is NavTarget.CreatePoll -> { - createPollEntryPoint.nodeBuilder(this, buildContext) - .params( - CreatePollEntryPoint.Params( - timelineMode = navTarget.timelineMode, - mode = CreatePollMode.NewPoll - ) - ) - .build() + createPollEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = CreatePollEntryPoint.Params( + timelineMode = navTarget.timelineMode, + mode = CreatePollMode.NewPoll + ), + ) } is NavTarget.EditPoll -> { - createPollEntryPoint.nodeBuilder(this, buildContext) - .params( - CreatePollEntryPoint.Params( - timelineMode = navTarget.timelineMode, - mode = CreatePollMode.EditPoll(eventId = navTarget.eventId) - ) - ) - .build() + createPollEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = CreatePollEntryPoint.Params( + timelineMode = navTarget.timelineMode, + mode = CreatePollMode.EditPoll(eventId = navTarget.eventId) + ), + ) } NavTarget.PinnedMessagesList -> { val callback = object : PinnedMessagesListNode.Callback { - override fun onEventClick(event: TimelineItem.Event) { + override fun handleEventClick(event: TimelineItem.Event) { processEventClick( timelineMode = Timeline.Mode.PinnedEvents, event = event, ) } - override fun onUserDataClick(userId: UserId) { - callbacks.forEach { it.onUserDataClick(userId) } + override fun navigateToRoomMemberDetails(userId: UserId) { + callback.navigateToRoomMemberDetails(userId) } - override fun onViewInTimelineClick(eventId: EventId) { - viewInTimeline(eventId) + override fun viewInTimeline(eventId: EventId) { + this@MessagesFlowNode.viewInTimeline(eventId) } - override fun onRoomPermalinkClick(data: PermalinkData.RoomLink) { - callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias)) } + override fun handlePermalinkClick(data: PermalinkData.RoomLink) { + callback.handlePermalinkClick(data, pushToBackstack = !room.matches(data.roomIdOrAlias)) } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } - override fun onForwardEventClick(eventId: EventId) { + override fun handleForwardEventClick(eventId: EventId) { backstack.push(NavTarget.ForwardEvent(eventId = eventId, fromPinnedEvents = true)) } } @@ -430,14 +438,14 @@ class MessagesFlowNode( focusedEventId = navTarget.focusedEventId, ) val callback = object : ThreadedMessagesNode.Callback { - override fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { + override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { return processEventClick( timelineMode = timelineMode, event = event, ) } - override fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { + override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { backstack.push( NavTarget.AttachmentPreview( attachment = attachments.first(), @@ -447,39 +455,39 @@ class MessagesFlowNode( ) } - override fun onUserDataClick(userId: UserId) { - callbacks.forEach { it.onUserDataClick(userId) } + override fun navigateToRoomMemberDetails(userId: UserId) { + callback.navigateToRoomMemberDetails(userId) } - override fun onPermalinkClick(data: PermalinkData) { - callbacks.forEach { it.onPermalinkClick(data, pushToBackstack = true) } + override fun handlePermalinkClick(data: PermalinkData) { + callback.handlePermalinkClick(data, pushToBackstack = true) } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } - override fun onForwardEventClick(eventId: EventId) { + override fun handleForwardEventClick(eventId: EventId) { backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false)) } - override fun onReportMessage(eventId: EventId, senderId: UserId) { + override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { backstack.push(NavTarget.ReportMessage(eventId, senderId)) } - override fun onSendLocationClick() { + override fun navigateToSendLocation() { backstack.push(NavTarget.SendLocation(Timeline.Mode.Thread(navTarget.threadRootId))) } - override fun onCreatePollClick() { + override fun navigateToCreatePoll() { backstack.push(NavTarget.CreatePoll(Timeline.Mode.Thread(navTarget.threadRootId))) } - override fun onEditPollClick(eventId: EventId) { + override fun navigateToEditPoll(eventId: EventId) { backstack.push(NavTarget.EditPoll(Timeline.Mode.Thread(navTarget.threadRootId), eventId)) } - override fun onJoinCallClick(roomId: RoomId) { + override fun navigateToRoomCall(roomId: RoomId) { val callType = CallType.RoomCall( sessionId = sessionId, roomId = roomId, @@ -488,7 +496,7 @@ class MessagesFlowNode( elementCallEntryPoint.startCall(callType) } - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { + override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { backstack.push(NavTarget.Thread(threadRootId, focusedEventId)) } } @@ -502,11 +510,7 @@ class MessagesFlowNode( roomIdOrAlias = room.roomId.toRoomIdOrAlias(), eventId = eventId, ) - callbacks.forEach { it.onPermalinkClick(permalinkData, pushToBackstack = false) } - } - - private fun forwardEvent(eventId: EventId) { - callbacks.forEach { it.forwardEvent(eventId) } + callback.handlePermalinkClick(permalinkData, pushToBackstack = false) } private fun processEventClick( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index 25fd7f222d..4a2189ccc7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -16,12 +16,12 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn import kotlinx.collections.immutable.ImmutableList interface MessagesNavigator { - fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun onForwardEventClick(eventId: EventId) - fun onReportContentClick(eventId: EventId, senderId: UserId) - fun onEditPollClick(eventId: EventId) - fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) - fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) - fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) - fun onNavigateUp() + fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) + fun forwardEvent(eventId: EventId) + fun navigateToReportMessage(eventId: EventId, senderId: UserId) + fun navigateToEditPoll(eventId: EventId) + fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) + fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) + fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) + fun close() } 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 e0e3e88891..558d5cf503 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 @@ -24,7 +24,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -48,8 +47,8 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.androidutils.system.toast import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.ApplicationContext @@ -93,13 +92,12 @@ class MessagesNode( private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer, private val roomMemberModerationRenderer: RoomMemberModerationRenderer, ) : Node(buildContext, plugins = plugins), MessagesNavigator { - private val callbacks = plugins() - data class Inputs( val focusedEventId: EventId?, ) : NodeInputs private val inputs = inputs() + private val callback: Callback = callback() private val timelineController = TimelineController(room, room.liveTimeline) private val presenter = presenterFactory.create( @@ -114,21 +112,21 @@ class MessagesNode( ) interface Callback : Plugin { - fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean - fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) - fun onUserDataClick(userId: UserId) - fun onPermalinkClick(data: PermalinkData) - fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun onForwardEventClick(eventId: EventId) - fun onReportMessage(eventId: EventId, senderId: UserId) - fun onSendLocationClick() - fun onCreatePollClick() - fun onEditPollClick(eventId: EventId) - fun onJoinCallClick(roomId: RoomId) - fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) - fun onRoomDetailsClick() - fun onViewAllPinnedEvents() - fun onViewKnockRequests() + fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean + fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) + fun navigateToRoomMemberDetails(userId: UserId) + fun handlePermalinkClick(data: PermalinkData) + fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) + fun forwardEvent(eventId: EventId) + fun navigateToReportMessage(eventId: EventId, senderId: UserId) + fun navigateToSendLocation() + fun navigateToCreatePoll() + fun navigateToEditPoll(eventId: EventId) + fun navigateToRoomCall(roomId: RoomId) + fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) + fun navigateToRoomDetails() + fun navigateToPinnedMessagesList() + fun navigateToKnockRequestsList() } override fun onBuilt() { @@ -143,32 +141,6 @@ class MessagesNode( ) } - private fun onRoomDetailsClick() { - callbacks.forEach { it.onRoomDetailsClick() } - } - - private fun onViewAllPinnedMessagesClick() { - callbacks.forEach { it.onViewAllPinnedEvents() } - } - - private fun onViewKnockRequestsClick() { - callbacks.forEach { it.onViewKnockRequests() } - } - - private fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { - // Note: cannot use `callbacks.all { it.onEventClick(event) }` because: - // - if callbacks is empty, it will return true and we want to return false. - // - if a callback returns false, the other callback will not be invoked. - return callbacks.takeIf { it.isNotEmpty() } - ?.map { it.onEventClick(timelineMode, event) } - ?.all { it } - .orFalse() - } - - private fun onUserDataClick(userId: UserId) { - callbacks.forEach { it.onUserDataClick(userId) } - } - private fun onLinkClick( activity: Activity, darkTheme: Boolean, @@ -180,7 +152,7 @@ class MessagesNode( is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.onUserDataClick(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { handleRoomLinkClick(permalink, eventSink) @@ -211,60 +183,48 @@ class MessagesNode( displaySameRoomToast() } } else { - callbacks.forEach { it.onPermalinkClick(roomLink) } + callback.handlePermalinkClick(roomLink) } } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.onShowEventDebugInfoClick(eventId, debugInfo) } + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + callback.navigateToEventDebugInfo(eventId, debugInfo) } - override fun onForwardEventClick(eventId: EventId) { - callbacks.forEach { it.onForwardEventClick(eventId) } + override fun forwardEvent(eventId: EventId) { + callback.forwardEvent(eventId) } - override fun onReportContentClick(eventId: EventId, senderId: UserId) { - callbacks.forEach { it.onReportMessage(eventId, senderId) } + override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { + callback.navigateToReportMessage(eventId, senderId) } - override fun onEditPollClick(eventId: EventId) { - callbacks.forEach { it.onEditPollClick(eventId) } + override fun navigateToEditPoll(eventId: EventId) { + callback.navigateToEditPoll(eventId) } - override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { - callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } + override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { + callback.navigateToPreviewAttachments(attachments, inReplyToEventId) } - override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { if (roomId == room.roomId) { displaySameRoomToast() } else { val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) - callbacks.forEach { it.onPermalinkClick(permalinkData) } + callback.handlePermalinkClick(permalinkData) } } - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { - callbacks.forEach { it.onOpenThread(threadRootId, focusedEventId) } - } - - private fun onSendLocationClick() { - callbacks.forEach { it.onSendLocationClick() } - } - - private fun onCreatePollClick() { - callbacks.forEach { it.onCreatePollClick() } - } - - private fun onJoinCallClick() { - callbacks.forEach { it.onJoinCallClick(room.roomId) } + override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { + callback.navigateToThread(threadRootId, focusedEventId) } private fun displaySameRoomToast() { context.toast(CommonStrings.screen_room_permalink_same_room_android) } - override fun onNavigateUp() = navigateUp() + override fun close() = navigateUp() @Composable override fun View(modifier: Modifier) { @@ -288,20 +248,20 @@ class MessagesNode( MessagesView( state = state, onBackClick = { state.eventSink(MessagesEvents.MarkAsFullyReadAndExit) }, - onRoomDetailsClick = this::onRoomDetailsClick, + onRoomDetailsClick = callback::navigateToRoomDetails, onEventContentClick = { isLive, event -> if (isLive) { - onEventClick(timelineController.mainTimelineMode(), event) + callback.handleEventClick(timelineController.mainTimelineMode(), event) } else { val detachedTimelineMode = timelineController.detachedTimelineMode() if (detachedTimelineMode != null) { - onEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event) } else { false } } }, - onUserDataClick = this::onUserDataClick, + onUserDataClick = callback::navigateToRoomMemberDetails, onLinkClick = { url, customTab -> onLinkClick( activity = activity, @@ -311,15 +271,15 @@ class MessagesNode( customTab = customTab, ) }, - onSendLocationClick = this::onSendLocationClick, - onCreatePollClick = this::onCreatePollClick, - onJoinCallClick = this::onJoinCallClick, - onViewAllPinnedMessagesClick = this::onViewAllPinnedMessagesClick, + onSendLocationClick = callback::navigateToSendLocation, + onCreatePollClick = callback::navigateToCreatePoll, + onJoinCallClick = { callback.navigateToRoomCall(room.roomId) }, + onViewAllPinnedMessagesClick = callback::navigateToPinnedMessagesList, modifier = modifier, knockRequestsBannerView = { knockRequestsBannerRenderer.View( modifier = Modifier, - onViewRequestsClick = this::onViewKnockRequestsClick + onViewRequestsClick = callback::navigateToKnockRequestsList, ) }, ) @@ -327,7 +287,7 @@ class MessagesNode( state = state.roomMemberModerationState, onSelectAction = { action, target -> when (action) { - is ModerationAction.DisplayProfile -> onUserDataClick(target.userId) + is ModerationAction.DisplayProfile -> callback.navigateToRoomMemberDetails(target.userId) else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target)) } }, 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 9056437218..acab975a45 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 @@ -251,7 +251,7 @@ class MessagesPresenter( if (!markingAsReadAndExiting.getAndSet(true)) { val latestEventId = room.liveTimeline.getLatestEventId().getOrElse { Timber.w(it, "Failed to get latest event id to mark as fully read") - navigator.onNavigateUp() + navigator.close() return@launch } latestEventId?.let { eventId -> @@ -259,7 +259,7 @@ class MessagesPresenter( markAsFullyRead(room.roomId, eventId) } } - navigator.onNavigateUp() + navigator.close() markingAsReadAndExiting.set(false) } } @@ -355,7 +355,7 @@ class MessagesPresenter( is TimelineItemThreadInfo.ThreadResponse -> targetEvent.threadInfo.threadRootId is TimelineItemThreadInfo.ThreadRoot, null -> targetEvent.eventId?.toThreadId() } ?: return@launch - navigator.onOpenThread(threadId, null) + navigator.navigateToThread(threadId, null) } else { handleActionReply(targetEvent, composerState, timelineProtectionState) } @@ -463,7 +463,7 @@ class MessagesPresenter( when (targetEvent.content) { is TimelineItemPollContent -> { if (targetEvent.eventId == null) return - navigator.onEditPollClick(targetEvent.eventId) + navigator.navigateToEditPoll(targetEvent.eventId) } else -> { val composerMode = MessageComposerMode.Edit( @@ -528,17 +528,17 @@ class MessagesPresenter( } private fun handleShowDebugInfoAction(event: TimelineItem.Event) { - navigator.onShowEventDebugInfoClick(event.eventId, event.debugInfo) + navigator.navigateToEventDebugInfo(event.eventId, event.debugInfo) } private fun handleForwardAction(event: TimelineItem.Event) { if (event.eventId == null) return - navigator.onForwardEventClick(event.eventId) + navigator.forwardEvent(event.eventId) } private fun handleReportAction(event: TimelineItem.Event) { if (event.eventId == null) return - navigator.onReportContentClick(event.eventId, event.senderId) + navigator.navigateToReportMessage(event.eventId, event.senderId) } private fun handleEndPollAction( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 192ff30f39..370b3853ea 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -122,7 +122,7 @@ class DefaultActionListPresenter( return ActionListState( target = target.value, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt index b62b4116e6..c0121b98b0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/video/DefaultMediaOptimizationSelectorPresenter.kt @@ -172,7 +172,7 @@ class DefaultMediaOptimizationSelectorPresenter( selectedVideoPreset = selectedVideoOptimizationPreset.dataOrNull(), displayMediaSelectorViews = displayMediaSelectorViews, displayVideoPresetSelectorDialog = displayVideoPresetSelectorDialog, - eventSink = { handleEvent(it) }, + eventSink = ::handleEvent, ) } 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 9505f0d758..f7dd029826 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 @@ -382,7 +382,7 @@ class MessageComposerPresenter( suggestions = suggestions.toImmutableList(), resolveMentionDisplay = resolveMentionDisplay, resolveAtRoomMentionDisplay = resolveAtRoomMentionDisplay, - eventSink = { handleEvents(it) }, + eventSink = ::handleEvents, ) } @@ -528,7 +528,7 @@ class MessageComposerPresenter( ) val mediaAttachment = Attachment.Media(localMedia) val inReplyToEventId = (messageComposerContext.composerMode as? MessageComposerMode.Reply)?.eventId - navigator.onPreviewAttachment(persistentListOf(mediaAttachment), inReplyToEventId) + navigator.navigateToPreviewAttachments(persistentListOf(mediaAttachment), inReplyToEventId) // Reset composer since the attachment will be sent in a separate flow messageComposerContext.composerMode = MessageComposerMode.Normal diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/DefaultPinnedEventsTimelineProvider.kt similarity index 93% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/DefaultPinnedEventsTimelineProvider.kt index 811516e022..f4641c8a66 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/DefaultPinnedEventsTimelineProvider.kt @@ -7,8 +7,9 @@ package io.element.android.features.messages.impl.pinned -import dev.zacsweers.metro.Inject +import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn +import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.mapState @@ -17,7 +18,6 @@ import io.element.android.libraries.matrix.api.room.CreateTimelineParams import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.timeline.Timeline -import io.element.android.libraries.matrix.api.timeline.TimelineProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -29,12 +29,12 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext @SingleIn(RoomScope::class) -@Inject -class PinnedEventsTimelineProvider( +@ContributesBinding(RoomScope::class) +class DefaultPinnedEventsTimelineProvider( private val room: JoinedRoom, private val syncService: SyncService, private val dispatchers: CoroutineDispatchers, -) : TimelineProvider { +) : PinnedEventsTimelineProvider { private val _timelineStateFlow: MutableStateFlow> = MutableStateFlow(AsyncData.Uninitialized) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt index 5833da56dc..c0f2cccb6f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject -import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider +import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.room.BaseRoom @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.onEach class PinnedMessagesBannerPresenter( private val room: BaseRoom, private val itemFactory: PinnedMessagesBannerItemFactory, - private val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider, + private val pinnedEventsTimelineProvider: DefaultPinnedEventsTimelineProvider, ) : Presenter { private val pinnedItems = mutableStateOf>>(AsyncData.Uninitialized) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNavigator.kt index d33ad54eed..7a890eac7b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNavigator.kt @@ -11,7 +11,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo interface PinnedMessagesListNavigator { - fun onViewInTimelineClick(eventId: EventId) - fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun onForwardEventClick(eventId: EventId) + fun viewInTimeline(eventId: EventId) + fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) + fun forwardEvent(eventId: EventId) } 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 8ba776c520..130d9fe678 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 @@ -17,7 +17,6 @@ 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 -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -27,6 +26,7 @@ import io.element.android.features.messages.impl.timeline.di.TimelineItemPresent import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UserId @@ -34,7 +34,6 @@ 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.Timeline import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(RoomScope::class) @@ -48,14 +47,15 @@ class PinnedMessagesListNode( private val permalinkParser: PermalinkParser, ) : Node(buildContext, plugins = plugins), PinnedMessagesListNavigator { interface Callback : Plugin { - fun onEventClick(event: TimelineItem.Event) - fun onUserDataClick(userId: UserId) - fun onViewInTimelineClick(eventId: EventId) - fun onRoomPermalinkClick(data: PermalinkData.RoomLink) - fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun onForwardEventClick(eventId: EventId) + fun handleEventClick(event: TimelineItem.Event) + fun navigateToRoomMemberDetails(userId: UserId) + fun viewInTimeline(eventId: EventId) + fun handlePermalinkClick(data: PermalinkData.RoomLink) + fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) + fun handleForwardEventClick(eventId: EventId) } + private val callback: Callback = callback() private val presenter = presenterFactory.create( navigator = this, actionListPresenter = actionListPresenterFactory.create( @@ -63,25 +63,16 @@ class PinnedMessagesListNode( timelineMode = Timeline.Mode.PinnedEvents, ) ) - private val callbacks = plugins() - - private fun onEventClick(event: TimelineItem.Event) { - return callbacks.forEach { it.onEventClick(event) } - } - - private fun onUserDataClick(user: MatrixUser) { - callbacks.forEach { it.onUserDataClick(user.userId) } - } private fun onLinkClick(context: Context, url: String) { when (val permalink = permalinkParser.parse(url)) { is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.onUserDataClick(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { - callbacks.forEach { it.onRoomPermalinkClick(permalink) } + callback.handlePermalinkClick(permalink) } is PermalinkData.FallbackLink, is PermalinkData.RoomEmailInviteLink -> { @@ -90,16 +81,16 @@ class PinnedMessagesListNode( } } - override fun onViewInTimelineClick(eventId: EventId) { - callbacks.forEach { it.onViewInTimelineClick(eventId) } + override fun viewInTimeline(eventId: EventId) { + callback.viewInTimeline(eventId) } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.onShowEventDebugInfoClick(eventId, debugInfo) } + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + callback.navigateToEventDebugInfo(eventId, debugInfo) } - override fun onForwardEventClick(eventId: EventId) { - callbacks.forEach { it.onForwardEventClick(eventId) } + override fun forwardEvent(eventId: EventId) { + callback.handleForwardEventClick(eventId) } @Composable @@ -113,8 +104,8 @@ class PinnedMessagesListNode( PinnedMessagesListView( state = state, onBackClick = ::navigateUp, - onEventClick = ::onEventClick, - onUserDataClick = ::onUserDataClick, + onEventClick = callback::handleEventClick, + onUserDataClick = { callback.navigateToRoomMemberDetails(it.userId) }, onLinkClick = { link -> onLinkClick(context, link.url) }, onLinkLongClick = { view.performHapticFeedback( 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 50652bb6e5..764286ce2e 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 @@ -26,7 +26,7 @@ import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.actionlist.ActionListState import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.link.LinkState -import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider +import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig @@ -66,7 +66,7 @@ class PinnedMessagesListPresenter( @Assisted private val navigator: PinnedMessagesListNavigator, private val room: JoinedRoom, timelineItemsFactoryCreator: TimelineItemsFactory.Creator, - private val timelineProvider: PinnedEventsTimelineProvider, + private val timelineProvider: DefaultPinnedEventsTimelineProvider, private val timelineProtectionPresenter: Presenter, private val linkPresenter: Presenter, private val snackbarDispatcher: SnackbarDispatcher, @@ -153,18 +153,18 @@ class PinnedMessagesListPresenter( ) = launch { when (action) { TimelineItemAction.ViewSource -> { - navigator.onShowEventDebugInfoClick(targetEvent.eventId, targetEvent.debugInfo) + navigator.navigateToEventDebugInfo(targetEvent.eventId, targetEvent.debugInfo) } TimelineItemAction.Forward -> { targetEvent.eventId?.let { eventId -> - navigator.onForwardEventClick(eventId) + navigator.forwardEvent(eventId) } } TimelineItemAction.Unpin -> handleUnpinAction(targetEvent) TimelineItemAction.ViewInTimeline -> { targetEvent.eventId?.let { eventId -> analyticsService.captureInteraction(Interaction.Name.PinnedMessageListViewTimeline) - navigator.onViewInTimelineClick(eventId) + navigator.viewInTimeline(eventId) } } else -> Unit diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index 9b5121eeb5..c6d5c46b61 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -22,7 +22,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -44,8 +43,8 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab import io.element.android.libraries.androidutils.system.openUrlInExternalApp import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.annotations.SessionCoroutineScope @@ -88,14 +87,13 @@ class ThreadedMessagesNode( private val permalinkParser: PermalinkParser, private val appNavigationStateService: AppNavigationStateService, ) : Node(buildContext, plugins = plugins), MessagesNavigator { - private val callbacks = plugins() - data class Inputs( val threadRootEventId: ThreadId, val focusedEventId: EventId?, ) : NodeInputs private val inputs = inputs() + private val callback: Callback = callback() // TODO use a loading state node to preload this instead of using `runBlocking` private val threadedTimeline = runBlocking { room.createTimeline(CreateTimelineParams.Threaded(threadRootEventId = inputs.threadRootEventId)).getOrThrow() } @@ -113,18 +111,18 @@ class ThreadedMessagesNode( ) interface Callback : Plugin { - fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean - fun onPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) - fun onUserDataClick(userId: UserId) - fun onPermalinkClick(data: PermalinkData) - fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun onForwardEventClick(eventId: EventId) - fun onReportMessage(eventId: EventId, senderId: UserId) - fun onSendLocationClick() - fun onCreatePollClick() - fun onEditPollClick(eventId: EventId) - fun onJoinCallClick(roomId: RoomId) - fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) + fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean + fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) + fun navigateToRoomMemberDetails(userId: UserId) + fun handlePermalinkClick(data: PermalinkData) + fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) + fun handleForwardEventClick(eventId: EventId) + fun navigateToReportMessage(eventId: EventId, senderId: UserId) + fun navigateToSendLocation() + fun navigateToCreatePoll() + fun navigateToEditPoll(eventId: EventId) + fun navigateToRoomCall(roomId: RoomId) + fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) } override fun onBuilt() { @@ -145,20 +143,6 @@ class ThreadedMessagesNode( ) } - private fun onEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean { - // Note: cannot use `callbacks.all { it.onEventClick(event) }` because: - // - if callbacks is empty, it will return true and we want to return false. - // - if a callback returns false, the other callback will not be invoked. - return callbacks.takeIf { it.isNotEmpty() } - ?.map { it.onEventClick(timelineMode, event) } - ?.all { it } - .orFalse() - } - - private fun onUserDataClick(userId: UserId) { - callbacks.forEach { it.onUserDataClick(userId) } - } - private fun onLinkClick( activity: Activity, darkTheme: Boolean, @@ -170,7 +154,7 @@ class ThreadedMessagesNode( is PermalinkData.UserLink -> { // Open the room member profile, it will fallback to // the user profile if the user is not in the room - callbacks.forEach { it.onUserDataClick(permalink.userId) } + callback.navigateToRoomMemberDetails(permalink.userId) } is PermalinkData.RoomLink -> { handleRoomLinkClick(permalink, eventSink) @@ -204,52 +188,40 @@ class ThreadedMessagesNode( navigateUp() } } else { - callbacks.forEach { it.onPermalinkClick(roomLink) } + callback.handlePermalinkClick(roomLink) } } - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { - callbacks.forEach { it.onShowEventDebugInfoClick(eventId, debugInfo) } + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + callback.navigateToEventDebugInfo(eventId, debugInfo) } - override fun onForwardEventClick(eventId: EventId) { - callbacks.forEach { it.onForwardEventClick(eventId) } + override fun forwardEvent(eventId: EventId) { + callback.handleForwardEventClick(eventId) } - override fun onReportContentClick(eventId: EventId, senderId: UserId) { - callbacks.forEach { it.onReportMessage(eventId, senderId) } + override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { + callback.navigateToReportMessage(eventId, senderId) } - override fun onEditPollClick(eventId: EventId) { - callbacks.forEach { it.onEditPollClick(eventId) } + override fun navigateToEditPoll(eventId: EventId) { + callback.navigateToEditPoll(eventId) } - override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { - callbacks.forEach { it.onPreviewAttachments(attachments, inReplyToEventId) } + override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { + callback.navigateToPreviewAttachments(attachments, inReplyToEventId) } - override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), eventId, viaParameters = serverNames.toImmutableList()) - callbacks.forEach { it.onPermalinkClick(permalinkData) } + callback.handlePermalinkClick(permalinkData) } - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { - callbacks.forEach { it.onOpenThread(threadRootId, focusedEventId) } + override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { + callback.navigateToThread(threadRootId, focusedEventId) } - override fun onNavigateUp() = navigateUp() - - private fun onSendLocationClick() { - callbacks.forEach { it.onSendLocationClick() } - } - - private fun onCreatePollClick() { - callbacks.forEach { it.onCreatePollClick() } - } - - private fun onJoinCallClick() { - callbacks.forEach { it.onJoinCallClick(room.roomId) } - } + override fun close() = navigateUp() @Composable override fun View(modifier: Modifier) { @@ -271,17 +243,17 @@ class ThreadedMessagesNode( onRoomDetailsClick = {}, onEventContentClick = { isLive, event -> if (isLive) { - onEventClick(timelineController.mainTimelineMode(), event) + callback.handleEventClick(timelineController.mainTimelineMode(), event) } else { val detachedTimelineMode = timelineController.detachedTimelineMode() if (detachedTimelineMode != null) { - onEventClick(detachedTimelineMode, event) + callback.handleEventClick(detachedTimelineMode, event) } else { false } } }, - onUserDataClick = this::onUserDataClick, + onUserDataClick = callback::navigateToRoomMemberDetails, onLinkClick = { url, customTab -> onLinkClick( activity = activity, @@ -291,9 +263,9 @@ class ThreadedMessagesNode( customTab = customTab, ) }, - onSendLocationClick = this::onSendLocationClick, - onCreatePollClick = this::onCreatePollClick, - onJoinCallClick = this::onJoinCallClick, + onSendLocationClick = callback::navigateToSendLocation, + onCreatePollClick = callback::navigateToCreatePoll, + onJoinCallClick = { callback.navigateToRoomCall(room.roomId) }, onViewAllPinnedMessagesClick = {}, modifier = modifier, knockRequestsBannerView = {}, 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 ae8e26bff4..6f9dab56c8 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 @@ -185,7 +185,7 @@ class TimelinePresenter( } } is TimelineEvents.EditPoll -> { - navigator.onEditPollClick(event.pollStartId) + navigator.navigateToEditPoll(event.pollStartId) } is TimelineEvents.FocusOnEvent -> sessionCoroutineScope.launch { focusRequestState.value = FocusRequestState.Requested(event.eventId, event.debounce) @@ -210,10 +210,10 @@ class TimelinePresenter( is TimelineEvents.NavigateToPredecessorOrSuccessorRoom -> { // Navigate to the predecessor or successor room val serverNames = calculateServerNamesForRoom(room) - navigator.onNavigateToRoom(event.roomId, null, serverNames) + navigator.navigateToRoom(event.roomId, null, serverNames) } is TimelineEvents.OpenThread -> { - navigator.onOpenThread( + navigator.navigateToThread( threadRootId = event.threadRootEventId, focusedEventId = event.focusedEvent, ) @@ -289,7 +289,7 @@ class TimelinePresenter( messageShield = messageShield.value, resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState, displayThreadSummaries = displayThreadSummaries, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } @@ -314,7 +314,7 @@ class TimelinePresenter( if (timelineController.mainTimelineMode() is Timeline.Mode.Thread && threadId == null) { // We are in a thread timeline, and the event isn't part of a thread, we need to navigate back to the room focusRequestState.value = FocusRequestState.None - navigator.onNavigateToRoom(room.roomId, eventId, calculateServerNamesForRoom(room)) + navigator.navigateToRoom(room.roomId, eventId, calculateServerNamesForRoom(room)) } else { Timber.tag(tag).d("Focusing on event $eventId - thread $threadId") timelineController.focusOnEvent(eventId, threadId) @@ -331,7 +331,7 @@ class TimelinePresenter( } else { focusRequestState.value = FocusRequestState.Success(eventId = result.threadId.asEventId()) // It's part of a thread we're not in, let's open it in another timeline - navigator.onOpenThread(result.threadId, eventId) + navigator.navigateToThread(result.threadId, eventId) } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt index 3d1cc93d58..bbddbb1066 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/CustomReactionPresenter.kt @@ -71,7 +71,7 @@ class CustomReactionPresenter( target = target.value, selectedEmoji = selectedEmoji, recentEmojis = recentEmojis, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt index 4354ef5b25..c3fb43d374 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryPresenter.kt @@ -48,7 +48,7 @@ class ReactionSummaryPresenter( } return ReactionSummaryState( target = targetWithAvatars.value, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt index 33316a134a..a520f1fee1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt @@ -36,7 +36,7 @@ class ReadReceiptBottomSheetPresenter : Presenter { return ReadReceiptBottomSheetState( selectedEvent = selectedEvent, - eventSink = { handleEvent(it) }, + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt index 0d9db51d98..ded49a3804 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/protection/TimelineProtectionPresenter.kt @@ -56,7 +56,7 @@ class TimelineProtectionPresenter( return TimelineProtectionState( protectionState = protectionState, - eventSink = { event -> handleEvent(event) } + eventSink = ::handleEvent, ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt index 88c0ca8020..e9502346b4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/DefaultMessagesEntryPointTest.kt @@ -10,26 +10,23 @@ package io.element.android.features.messages.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.compose.runtime.Composable import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType -import io.element.android.features.call.api.ElementCallEntryPoint -import io.element.android.features.forward.api.ForwardEntryPoint -import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint -import io.element.android.features.location.api.SendLocationEntryPoint -import io.element.android.features.location.api.ShowLocationEntryPoint +import io.element.android.features.call.test.FakeElementCallEntryPoint +import io.element.android.features.forward.test.FakeForwardEntryPoint +import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint import io.element.android.features.location.test.FakeLocationService +import io.element.android.features.location.test.FakeSendLocationEntryPoint +import io.element.android.features.location.test.FakeShowLocationEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.impl.pinned.banner.createPinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.createTimelineController -import io.element.android.features.poll.api.create.CreatePollEntryPoint +import io.element.android.features.poll.test.create.FakeCreatePollEntryPoint import io.element.android.libraries.dateformatter.test.FakeDateFormatter 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.permalink.PermalinkData -import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID @@ -37,7 +34,7 @@ import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService import io.element.android.libraries.matrix.ui.messages.RoomMemberProfilesCache import io.element.android.libraries.matrix.ui.messages.RoomNamesCache -import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.libraries.mediaviewer.test.FakeMediaViewerEntryPoint import io.element.android.libraries.textcomposer.mentions.MentionSpanTheme import io.element.android.libraries.textcomposer.mentions.MentionSpanUpdater import io.element.android.services.analytics.test.FakeAnalyticsService @@ -64,36 +61,12 @@ class DefaultMessagesEntryPointTest { plugins = plugins, roomListService = FakeRoomListService(), sessionId = A_SESSION_ID, - sendLocationEntryPoint = object : SendLocationEntryPoint { - override fun builder(timelineMode: Timeline.Mode) = lambdaError() - }, - showLocationEntryPoint = object : ShowLocationEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, inputs: ShowLocationEntryPoint.Inputs) = lambdaError() - }, - createPollEntryPoint = object : CreatePollEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - elementCallEntryPoint = object : ElementCallEntryPoint { - override fun startCall(callType: CallType) = lambdaError() - override suspend fun handleIncomingCall( - callType: CallType.RoomCall, - eventId: EventId, - senderId: UserId, - roomName: String?, - senderName: String?, - avatarUrl: String?, - timestamp: Long, - expirationTimestamp: Long, - notificationChannelId: String, - textContent: String?, - ) = lambdaError() - }, - mediaViewerEntryPoint = object : MediaViewerEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - forwardEntryPoint = object : ForwardEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + sendLocationEntryPoint = FakeSendLocationEntryPoint(), + showLocationEntryPoint = FakeShowLocationEntryPoint(), + createPollEntryPoint = FakeCreatePollEntryPoint(), + elementCallEntryPoint = FakeElementCallEntryPoint(), + mediaViewerEntryPoint = FakeMediaViewerEntryPoint(), + forwardEntryPoint = FakeForwardEntryPoint(), analyticsService = FakeAnalyticsService(), locationService = FakeLocationService(), room = FakeBaseRoom(), @@ -108,26 +81,26 @@ class DefaultMessagesEntryPointTest { mentionSpanTheme = MentionSpanTheme(A_USER_ID), pinnedEventsTimelineProvider = createPinnedEventsTimelineProvider(), timelineController = createTimelineController(), - knockRequestsListEntryPoint = object : KnockRequestsListEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + knockRequestsListEntryPoint = FakeKnockRequestsListEntryPoint(), dateFormatter = FakeDateFormatter(), coroutineDispatchers = testCoroutineDispatchers(), ) } val callback = object : MessagesEntryPoint.Callback { - override fun onRoomDetailsClick() = lambdaError() - override fun onUserDataClick(userId: UserId) = lambdaError() - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() - override fun forwardEvent(eventId: EventId) = lambdaError() - override fun openRoom(roomId: RoomId) = lambdaError() + override fun navigateToRoomDetails() = lambdaError() + override fun navigateToRoomMemberDetails(userId: UserId) = lambdaError() + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() + override fun navigateToRoom(roomId: RoomId) = lambdaError() } val initialTarget = MessagesEntryPoint.InitialTarget.Messages(focusedEventId = AN_EVENT_ID) val params = MessagesEntryPoint.Params(initialTarget) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(MessagesFlowNode::class.java) assertThat(result.plugins).contains(MessagesEntryPoint.Params(initialTarget)) assertThat(result.plugins).contains(callback) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index c26e7a83e2..9fd6e4af2a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -24,37 +24,37 @@ class FakeMessagesNavigator( private val onPreviewAttachmentLambda: (attachments: ImmutableList, inReplyToEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, private val onNavigateToRoomLambda: (roomId: RoomId, threadId: EventId?, serverNames: List) -> Unit = { _, _, _ -> lambdaError() }, private val onOpenThreadLambda: (threadRootId: ThreadId, focusedEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, - private val onNavigateUpLambda: () -> Unit = { lambdaError() }, + private val closeLambda: () -> Unit = { lambdaError() }, ) : MessagesNavigator { - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickLambda(eventId, debugInfo) } - override fun onForwardEventClick(eventId: EventId) { + override fun forwardEvent(eventId: EventId) { onForwardEventClickLambda(eventId) } - override fun onReportContentClick(eventId: EventId, senderId: UserId) { + override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { onReportContentClickLambda(eventId, senderId) } - override fun onEditPollClick(eventId: EventId) { + override fun navigateToEditPoll(eventId: EventId) { onEditPollClickLambda(eventId) } - override fun onPreviewAttachment(attachments: ImmutableList, inReplyToEventId: EventId?) { + override fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) { onPreviewAttachmentLambda(attachments, inReplyToEventId) } - override fun onNavigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { + override fun navigateToRoom(roomId: RoomId, eventId: EventId?, serverNames: List) { onNavigateToRoomLambda(roomId, eventId, serverNames) } - override fun onOpenThread(threadRootId: ThreadId, focusedEventId: EventId?) { + override fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?) { onOpenThreadLambda(threadRootId, focusedEventId) } - override fun onNavigateUp() { - onNavigateUpLambda() + override fun close() { + closeLambda() } } 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 34623a0c33..2dd98cc96f 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 @@ -1256,8 +1256,8 @@ class MessagesPresenterTest { fun `present - handle MarkAsFullyReadAndExit marks the room as fully read and navigates up`() = runTest { val markAsFullyReadRecorder = lambdaRecorder { _, _ -> } val markAsFullyReadUseCase = FakeMarkAsFullyRead(markAsFullyReadRecorder) - val onNavigateUpRecorder = lambdaRecorder {} - val navigator = FakeMessagesNavigator(onNavigateUpLambda = onNavigateUpRecorder) + val closeLambda = lambdaRecorder {} + val navigator = FakeMessagesNavigator(closeLambda = closeLambda) val presenter = createMessagesPresenter( timeline = FakeTimeline(getLatestEventIdResult = { Result.success(AN_EVENT_ID) }), @@ -1271,7 +1271,7 @@ class MessagesPresenterTest { runCurrent() markAsFullyReadRecorder.assertions().isCalledOnce() - onNavigateUpRecorder.assertions().isCalledOnce() + closeLambda.assertions().isCalledOnce() cancelAndIgnoreRemainingEvents() } @@ -1280,8 +1280,8 @@ class MessagesPresenterTest { @Test fun `present - handle MarkAsFullyReadAndExit still navigates up if marking as read fails`() = runTest { val markAsFullyReadUseCase = FakeMarkAsFullyRead { _, _ -> error("boom") } - val onNavigateUpRecorder = lambdaRecorder {} - val navigator = FakeMessagesNavigator(onNavigateUpLambda = onNavigateUpRecorder) + val closeLambda = lambdaRecorder {} + val navigator = FakeMessagesNavigator(closeLambda = closeLambda) val presenter = createMessagesPresenter( timeline = FakeTimeline(getLatestEventIdResult = { Result.success(AN_EVENT_ID) }), @@ -1294,7 +1294,7 @@ class MessagesPresenterTest { runCurrent() - onNavigateUpRecorder.assertions().isCalledOnce() + closeLambda.assertions().isCalledOnce() cancelAndIgnoreRemainingEvents() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt index 38182dec1d..b1be33d928 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt @@ -8,7 +8,7 @@ package io.element.android.features.messages.impl.pinned.banner import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider +import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.libraries.eventformatter.test.FakePinnedMessagesBannerFormatter import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.sync.SyncService @@ -195,7 +195,7 @@ class PinnedMessagesBannerPresenterTest { internal fun TestScope.createPinnedEventsTimelineProvider( room: JoinedRoom = FakeJoinedRoom(), syncService: SyncService = FakeSyncService(), -) = PinnedEventsTimelineProvider( +) = DefaultPinnedEventsTimelineProvider( room = room, syncService = syncService, dispatchers = testCoroutineDispatchers(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/FakePinnedMessagesListNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/FakePinnedMessagesListNavigator.kt index bf0a24dd5a..fba3803806 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/FakePinnedMessagesListNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/FakePinnedMessagesListNavigator.kt @@ -12,17 +12,17 @@ import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugIn class FakePinnedMessagesListNavigator : PinnedMessagesListNavigator { var onViewInTimelineClickLambda: ((EventId) -> Unit)? = null - override fun onViewInTimelineClick(eventId: EventId) { + override fun viewInTimeline(eventId: EventId) { onViewInTimelineClickLambda?.invoke(eventId) } var onShowEventDebugInfoClickLambda: ((EventId?, TimelineItemDebugInfo) -> Unit)? = null - override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { + override fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) { onShowEventDebugInfoClickLambda?.invoke(eventId, debugInfo) } var onForwardEventClickLambda: ((EventId) -> Unit)? = null - override fun onForwardEventClick(eventId: EventId) { + override fun forwardEvent(eventId: EventId) { onForwardEventClickLambda?.invoke(eventId) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt index 07778ab381..5087bd3558 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt @@ -13,7 +13,7 @@ import io.element.android.features.messages.impl.actionlist.anActionListState import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.fixtures.aTimelineItemsFactoryCreator import io.element.android.features.messages.impl.link.aLinkState -import io.element.android.features.messages.impl.pinned.PinnedEventsTimelineProvider +import io.element.android.features.messages.impl.pinned.DefaultPinnedEventsTimelineProvider import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -300,7 +300,7 @@ class PinnedMessagesListPresenterTest { analyticsService: AnalyticsService = FakeAnalyticsService(), featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), ): PinnedMessagesListPresenter { - val timelineProvider = PinnedEventsTimelineProvider( + val timelineProvider = DefaultPinnedEventsTimelineProvider( room = room, syncService = syncService, dispatchers = testCoroutineDispatchers(), diff --git a/features/messages/test/build.gradle.kts b/features/messages/test/build.gradle.kts index 93f5166f29..be94beb01b 100644 --- a/features/messages/test/build.gradle.kts +++ b/features/messages/test/build.gradle.kts @@ -23,4 +23,5 @@ dependencies { implementation(projects.libraries.preferences.api) implementation(projects.libraries.voicerecorder.test) implementation(projects.services.analytics.test) + implementation(projects.tests.testutils) } diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/FakeMessagesEntryPoint.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/FakeMessagesEntryPoint.kt new file mode 100644 index 0000000000..8cd8e6dc89 --- /dev/null +++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/FakeMessagesEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.messages.api.MessagesEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeMessagesEntryPoint : MessagesEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: MessagesEntryPoint.Params, + callback: MessagesEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt index 348f2f6c60..f1f4ee4f5c 100644 --- a/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt +++ b/features/poll/api/src/main/kotlin/io/element/android/features/poll/api/create/CreatePollEntryPoint.kt @@ -18,10 +18,9 @@ interface CreatePollEntryPoint : FeatureEntryPoint { val mode: CreatePollMode, ) - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + ): Node } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt index d8df6c2d01..1e41cdc59e 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.poll.impl.create import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.poll.api.create.CreatePollEntryPoint @@ -17,18 +16,14 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultCreatePollEntryPoint : CreatePollEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): CreatePollEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : CreatePollEntryPoint.NodeBuilder { - override fun params(params: CreatePollEntryPoint.Params): CreatePollEntryPoint.NodeBuilder { - plugins += CreatePollNode.Inputs(timelineMode = params.timelineMode, mode = params.mode) - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: CreatePollEntryPoint.Params, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(CreatePollNode.Inputs(timelineMode = params.timelineMode, mode = params.mode)) + ) } } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt index 19142508a1..6b43a06d01 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryFlowNode.kt @@ -53,18 +53,18 @@ class PollHistoryFlowNode( override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { is NavTarget.EditPoll -> { - createPollEntryPoint.nodeBuilder(this, buildContext) - .params( - CreatePollEntryPoint.Params( + createPollEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = CreatePollEntryPoint.Params( timelineMode = Timeline.Mode.Live, mode = CreatePollMode.EditPoll(eventId = navTarget.pollStartEventId) - ) ) - .build() + ) } NavTarget.Root -> { val callback = object : PollHistoryNode.Callback { - override fun onEditPoll(pollStartEventId: EventId) { + override fun navigateToEditPoll(pollStartEventId: EventId) { backstack.push(NavTarget.EditPoll(pollStartEventId)) } } diff --git a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt index 3fdfdb921f..714e7763e6 100644 --- a/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt +++ b/features/poll/impl/src/main/kotlin/io/element/android/features/poll/impl/history/PollHistoryNode.kt @@ -12,10 +12,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId @@ -30,19 +30,17 @@ class PollHistoryNode( plugins = plugins, ) { interface Callback : Plugin { - fun onEditPoll(pollStartEventId: EventId) + fun navigateToEditPoll(pollStartEventId: EventId) } - private fun onEditPoll(pollStartEventId: EventId) { - plugins().forEach { it.onEditPoll(pollStartEventId) } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { PollHistoryView( state = presenter.present(), modifier = modifier, - onEditPoll = ::onEditPoll, + onEditPoll = callback::navigateToEditPoll, goBack = this::navigateUp, ) } diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPointTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPointTest.kt index bf77cb4535..464fae98a0 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPointTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/DefaultCreatePollEntryPointTest.kt @@ -53,9 +53,11 @@ class DefaultCreatePollEntryPointTest { timelineMode = Timeline.Mode.Live, mode = CreatePollMode.NewPoll, ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + ) assertThat(result).isInstanceOf(CreatePollNode::class.java) assertThat(result.plugins).contains(CreatePollNode.Inputs(timelineMode = params.timelineMode, mode = params.mode)) } diff --git a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPointTest.kt b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPointTest.kt index dfab33e837..dfee9a7320 100644 --- a/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPointTest.kt +++ b/features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/history/DefaultPollHistoryEntryPointTest.kt @@ -9,11 +9,9 @@ package io.element.android.features.poll.impl.history import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.poll.api.create.CreatePollEntryPoint -import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.features.poll.test.create.FakeCreatePollEntryPoint import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -33,9 +31,7 @@ class DefaultPollHistoryEntryPointTest { PollHistoryFlowNode( buildContext = buildContext, plugins = plugins, - createPollEntryPoint = object : CreatePollEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - } + createPollEntryPoint = FakeCreatePollEntryPoint(), ) } val result = entryPoint.createNode(parentNode, BuildContext.root(null)) diff --git a/features/poll/test/build.gradle.kts b/features/poll/test/build.gradle.kts index 150a0bd3fc..35434f84a3 100644 --- a/features/poll/test/build.gradle.kts +++ b/features/poll/test/build.gradle.kts @@ -17,4 +17,5 @@ dependencies { implementation(projects.libraries.matrix.api) api(projects.features.poll.api) implementation(libs.kotlinx.collections.immutable) + implementation(projects.tests.testutils) } diff --git a/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/create/FakeCreatePollEntryPoint.kt b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/create/FakeCreatePollEntryPoint.kt new file mode 100644 index 0000000000..96a63225b2 --- /dev/null +++ b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/create/FakeCreatePollEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.poll.test.create + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.poll.api.create.CreatePollEntryPoint +import io.element.android.features.poll.api.create.CreatePollEntryPoint.Params +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeCreatePollEntryPoint : CreatePollEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + ): Node = lambdaError() +} diff --git a/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/history/FakePollHistoryEntryPoint.kt b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/history/FakePollHistoryEntryPoint.kt new file mode 100644 index 0000000000..725b5dc0ee --- /dev/null +++ b/features/poll/test/src/main/kotlin/io/element/android/features/poll/test/history/FakePollHistoryEntryPoint.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.poll.test.history + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.poll.api.history.PollHistoryEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakePollHistoryEntryPoint : PollHistoryEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + ): Node = lambdaError() +} diff --git a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt index c0affde2df..5bb8a68a99 100644 --- a/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt +++ b/features/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/PreferencesEntryPoint.kt @@ -31,19 +31,18 @@ interface PreferencesEntryPoint : FeatureEntryPoint { data class Params(val initialElement: InitialTarget) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { - fun onAddAccount() - fun onOpenBugReport() - fun onSecureBackupClick() - fun onOpenRoomNotificationSettings(roomId: RoomId) - fun navigateTo(roomId: RoomId, eventId: EventId) + fun navigateToAddAccount() + fun navigateToBugReport() + fun navigateToSecureBackup() + fun navigateToRoomNotificationSettings(roomId: RoomId) + fun navigateToEvent(roomId: RoomId, eventId: EventId) } } diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index 6858ebef51..31a7f87440 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -102,8 +102,13 @@ dependencies { testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) + testImplementation(projects.libraries.roomselect.test) + testImplementation(projects.libraries.troubleshoot.test) + testImplementation(projects.features.deactivation.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.invite.test) + testImplementation(projects.features.licenses.test) + testImplementation(projects.features.lockscreen.test) testImplementation(projects.features.rageshake.test) testImplementation(projects.features.logout.test) testImplementation(projects.libraries.indicator.test) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt index 7d32b3d8d9..ffc9a2b798 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.preferences.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.preferences.api.PreferencesEntryPoint @@ -17,24 +16,16 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultPreferencesEntryPoint : PreferencesEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): PreferencesEntryPoint.NodeBuilder { - return object : PreferencesEntryPoint.NodeBuilder { - val plugins = ArrayList() - - override fun params(params: PreferencesEntryPoint.Params): PreferencesEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun callback(callback: PreferencesEntryPoint.Callback): PreferencesEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: PreferencesEntryPoint.Params, + callback: PreferencesEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback) + ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt index e4ba87c43a..c8520c4f27 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -38,6 +37,7 @@ import io.element.android.features.preferences.impl.user.editprofile.EditUserPro import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.canPop +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.EventId @@ -116,63 +116,65 @@ class PreferencesFlowNode( data object OssLicenses : NavTarget } + private val callback: PreferencesEntryPoint.Callback = callback() + override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : PreferencesRootNode.Callback { - override fun onAddAccount() { - plugins().forEach { it.onAddAccount() } + override fun navigateToAddAccount() { + callback.navigateToAddAccount() } - override fun onOpenBugReport() { - plugins().forEach { it.onOpenBugReport() } + override fun navigateToBugReport() { + callback.navigateToBugReport() } - override fun onSecureBackupClick() { - plugins().forEach { it.onSecureBackupClick() } + override fun navigateToSecureBackup() { + callback.navigateToSecureBackup() } - override fun onOpenAnalytics() { + override fun navigateToAnalyticsSettings() { backstack.push(NavTarget.AnalyticsSettings) } - override fun onOpenAbout() { + override fun navigateToAbout() { backstack.push(NavTarget.About) } - override fun onOpenDeveloperSettings() { + override fun navigateToDeveloperSettings() { backstack.push(NavTarget.DeveloperSettings) } - override fun onOpenNotificationSettings() { + override fun navigateToNotificationSettings() { backstack.push(NavTarget.NotificationSettings) } - override fun onOpenLockScreenSettings() { + override fun navigateToLockScreenSettings() { backstack.push(NavTarget.LockScreenSettings) } - override fun onOpenAdvancedSettings() { + override fun navigateToAdvancedSettings() { backstack.push(NavTarget.AdvancedSettings) } - override fun onOpenLabs() { + override fun navigateToLabs() { backstack.push(NavTarget.Labs) } - override fun onOpenUserProfile(matrixUser: MatrixUser) { + override fun navigateToUserProfile(matrixUser: MatrixUser) { backstack.push(NavTarget.UserProfile(matrixUser)) } - override fun onOpenBlockedUsers() { + override fun navigateToBlockedUsers() { backstack.push(NavTarget.BlockedUsers) } - override fun onSignOutClick() { + override fun startSignOutFlow() { backstack.push(NavTarget.SignOut) } - override fun onOpenAccountDeactivation() { + override fun startAccountDeactivationFlow() { backstack.push(NavTarget.AccountDeactivation) } } @@ -180,7 +182,7 @@ class PreferencesFlowNode( } NavTarget.DeveloperSettings -> { val developerSettingsCallback = object : DeveloperSettingsNode.Callback { - override fun onPushHistoryClick() { + override fun navigateToPushHistory() { backstack.push(NavTarget.PushHistory) } } @@ -191,7 +193,7 @@ class PreferencesFlowNode( } NavTarget.About -> { val callback = object : AboutNode.Callback { - override fun openOssLicenses() { + override fun navigateToOssLicenses() { backstack.push(NavTarget.OssLicenses) } } @@ -202,19 +204,21 @@ class PreferencesFlowNode( } NavTarget.NotificationSettings -> { val notificationSettingsCallback = object : NotificationSettingsNode.Callback { - override fun editDefaultNotificationMode(isOneToOne: Boolean) { + override fun navigateToEditDefaultNotificationSetting(isOneToOne: Boolean) { backstack.push(NavTarget.EditDefaultNotificationSetting(isOneToOne)) } - override fun onTroubleshootNotificationsClick() { + override fun navigateToTroubleshootNotifications() { backstack.push(NavTarget.TroubleshootNotifications) } } createNode(buildContext, listOf(notificationSettingsCallback)) } NavTarget.TroubleshootNotifications -> { - notificationTroubleShootEntryPoint.nodeBuilder(this, buildContext) - .callback(object : NotificationTroubleShootEntryPoint.Callback { + notificationTroubleShootEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = object : NotificationTroubleShootEntryPoint.Callback { override fun onDone() { if (backstack.canPop()) { backstack.pop() @@ -223,15 +227,17 @@ class PreferencesFlowNode( } } - override fun openIgnoredUsers() { + override fun navigateToBlockedUsers() { backstack.push(NavTarget.BlockedUsers) } - }) - .build() + }, + ) } NavTarget.PushHistory -> { - pushHistoryEntryPoint.nodeBuilder(this, buildContext) - .callback(object : PushHistoryEntryPoint.Callback { + pushHistoryEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = object : PushHistoryEntryPoint.Callback { override fun onDone() { if (backstack.canPop()) { backstack.pop() @@ -240,16 +246,16 @@ class PreferencesFlowNode( } } - override fun navigateTo(roomId: RoomId, eventId: EventId) { - plugins().forEach { it.navigateTo(roomId, eventId) } + override fun navigateToEvent(roomId: RoomId, eventId: EventId) { + callback.navigateToEvent(roomId, eventId) } - }) - .build() + }, + ) } is NavTarget.EditDefaultNotificationSetting -> { val callback = object : EditDefaultNotificationSettingNode.Callback { - override fun openRoomNotificationSettings(roomId: RoomId) { - plugins().forEach { it.onOpenRoomNotificationSettings(roomId) } + override fun navigateToRoomNotificationSettings(roomId: RoomId) { + callback.navigateToRoomNotificationSettings(roomId) } } val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne) @@ -263,20 +269,31 @@ class PreferencesFlowNode( createNode(buildContext, listOf(inputs)) } NavTarget.LockScreenSettings -> { - lockScreenEntryPoint.nodeBuilder(this, buildContext, LockScreenEntryPoint.Target.Settings).build() + lockScreenEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + navTarget = LockScreenEntryPoint.Target.Settings, + callback = object : LockScreenEntryPoint.Callback { + override fun onSetupDone() { + // No op + } + } + ) } NavTarget.BlockedUsers -> { createNode(buildContext) } NavTarget.SignOut -> { val callBack: LogoutEntryPoint.Callback = object : LogoutEntryPoint.Callback { - override fun onChangeRecoveryKeyClick() { - plugins().forEach { it.onSecureBackupClick() } + override fun navigateToSecureBackup() { + callback.navigateToSecureBackup() } } - logoutEntryPoint.nodeBuilder(this, buildContext) - .callback(callBack) - .build() + logoutEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callBack, + ) } is NavTarget.OssLicenses -> { openSourceLicensesEntryPoint.createNode(this, buildContext) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt index 37de32bab7..a4e67f0ef3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/about/AboutNode.kt @@ -19,6 +19,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.compound.theme.ElementTheme import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -29,9 +30,11 @@ class AboutNode( private val presenter: AboutPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun openOssLicenses() + fun navigateToOssLicenses() } + private val callback: Callback = callback() + private fun onElementLegalClick( activity: Activity, darkTheme: Boolean, @@ -51,9 +54,7 @@ class AboutNode( onElementLegalClick = { elementLegal -> onElementLegalClick(activity, isDark, elementLegal) }, - onOpenSourceLicensesClick = { - plugins.filterIsInstance().forEach { it.openOssLicenses() } - }, + onOpenSourceLicensesClick = callback::navigateToOssLicenses, modifier = modifier ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt index 6208d0123e..385e99c3e3 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsNode.kt @@ -14,10 +14,10 @@ import com.airbnb.android.showkase.models.Showkase 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.di.SessionScope @@ -29,14 +29,10 @@ class DeveloperSettingsNode( private val presenter: DeveloperSettingsPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onPushHistoryClick() + fun navigateToPushHistory() } - private val callbacks = plugins() - - private fun onPushHistoryClick() { - callbacks.forEach { it.onPushHistoryClick() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -51,7 +47,7 @@ class DeveloperSettingsNode( state = state, modifier = modifier, onOpenShowkase = ::openShowkase, - onPushHistoryClick = ::onPushHistoryClick, + onPushHistoryClick = callback::navigateToPushHistory, onBackClick = ::navigateUp ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt index f488889c5b..b2338c7cc2 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsNode.kt @@ -12,10 +12,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -26,28 +26,20 @@ class NotificationSettingsNode( private val presenter: NotificationSettingsPresenter, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun editDefaultNotificationMode(isOneToOne: Boolean) - fun onTroubleshootNotificationsClick() + fun navigateToEditDefaultNotificationSetting(isOneToOne: Boolean) + fun navigateToTroubleshootNotifications() } - private val callbacks = plugins() - - private fun openEditDefault(isOneToOne: Boolean) { - callbacks.forEach { it.editDefaultNotificationMode(isOneToOne) } - } - - private fun onTroubleshootNotificationsClick() { - callbacks.forEach { it.onTroubleshootNotificationsClick() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() NotificationSettingsView( state = state, - onOpenEditDefault = { openEditDefault(isOneToOne = it) }, + onOpenEditDefault = callback::navigateToEditDefaultNotificationSetting, onBackClick = ::navigateUp, - onTroubleshootNotificationsClick = ::onTroubleshootNotificationsClick, + onTroubleshootNotificationsClick = callback::navigateToTroubleshootNotifications, modifier = modifier, ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt index ccba221d9a..823b3657e0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -29,27 +29,23 @@ class EditDefaultNotificationSettingNode( presenterFactory: EditDefaultNotificationSettingPresenter.Factory ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun openRoomNotificationSettings(roomId: RoomId) + fun navigateToRoomNotificationSettings(roomId: RoomId) } data class Inputs( val isOneToOne: Boolean ) : NodeInputs + private val callback: Callback = callback() private val inputs = inputs() - private val callbacks = plugins() private val presenter = presenterFactory.create(inputs.isOneToOne) - private fun openRoomNotificationSettings(roomId: RoomId) { - callbacks.forEach { it.openRoomNotificationSettings(roomId) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() EditDefaultNotificationSettingView( state = state, - openRoomNotificationSettings = { openRoomNotificationSettings(it) }, + openRoomNotificationSettings = callback::navigateToRoomNotificationSettings, onBackClick = ::navigateUp, modifier = modifier, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt index 67d50a76f0..4d3f482e7e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt @@ -14,7 +14,6 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -22,6 +21,7 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.logout.api.direct.DirectLogoutEvents import io.element.android.features.logout.api.direct.DirectLogoutView import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.user.MatrixUser @@ -34,53 +34,23 @@ class PreferencesRootNode( private val directLogoutView: DirectLogoutView, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onAddAccount() - fun onOpenBugReport() - fun onSecureBackupClick() - fun onOpenAnalytics() - fun onOpenAbout() - fun onOpenDeveloperSettings() - fun onOpenNotificationSettings() - fun onOpenLockScreenSettings() - fun onOpenAdvancedSettings() - fun onOpenLabs() - fun onOpenUserProfile(matrixUser: MatrixUser) - fun onOpenBlockedUsers() - fun onSignOutClick() - fun onOpenAccountDeactivation() + fun navigateToAddAccount() + fun navigateToBugReport() + fun navigateToSecureBackup() + fun navigateToAnalyticsSettings() + fun navigateToAbout() + fun navigateToDeveloperSettings() + fun navigateToNotificationSettings() + fun navigateToLockScreenSettings() + fun navigateToAdvancedSettings() + fun navigateToLabs() + fun navigateToUserProfile(matrixUser: MatrixUser) + fun navigateToBlockedUsers() + fun startSignOutFlow() + fun startAccountDeactivationFlow() } - private fun onAddAccount() { - plugins().forEach { it.onAddAccount() } - } - - private fun onOpenBugReport() { - plugins().forEach { it.onOpenBugReport() } - } - - private fun onSecureBackupClick() { - plugins().forEach { it.onSecureBackupClick() } - } - - private fun onOpenDeveloperSettings() { - plugins().forEach { it.onOpenDeveloperSettings() } - } - - private fun onOpenAdvancedSettings() { - plugins().forEach { it.onOpenAdvancedSettings() } - } - - private fun onOpenLabs() { - plugins().forEach { it.onOpenLabs() } - } - - private fun onOpenAnalytics() { - plugins().forEach { it.onOpenAnalytics() } - } - - private fun onOpenAbout() { - plugins().forEach { it.onOpenAbout() } - } + private val callback: Callback = callback() private fun onManageAccountClick( activity: Activity, @@ -96,30 +66,6 @@ class PreferencesRootNode( } } - private fun onOpenNotificationSettings() { - plugins().forEach { it.onOpenNotificationSettings() } - } - - private fun onOpenLockScreenSettings() { - plugins().forEach { it.onOpenLockScreenSettings() } - } - - private fun onOpenUserProfile(matrixUser: MatrixUser) { - plugins().forEach { it.onOpenUserProfile(matrixUser) } - } - - private fun onOpenBlockedUsers() { - plugins().forEach { it.onOpenBlockedUsers() } - } - - private fun onSignOutClick() { - plugins().forEach { it.onSignOutClick() } - } - - private fun onOpenAccountDeactivation() { - plugins().forEach { it.onOpenAccountDeactivation() } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -129,27 +75,27 @@ class PreferencesRootNode( state = state, modifier = modifier, onBackClick = this::navigateUp, - onAddAccountClick = this::onAddAccount, - onOpenRageShake = this::onOpenBugReport, - onOpenAnalytics = this::onOpenAnalytics, - onOpenAbout = this::onOpenAbout, - onSecureBackupClick = this::onSecureBackupClick, - onOpenDeveloperSettings = this::onOpenDeveloperSettings, - onOpenAdvancedSettings = this::onOpenAdvancedSettings, - onOpenLabs = this::onOpenLabs, + onAddAccountClick = callback::navigateToAddAccount, + onOpenRageShake = callback::navigateToBugReport, + onOpenAnalytics = callback::navigateToAnalyticsSettings, + onOpenAbout = callback::navigateToAbout, + onSecureBackupClick = callback::navigateToSecureBackup, + onOpenDeveloperSettings = callback::navigateToDeveloperSettings, + onOpenAdvancedSettings = callback::navigateToAdvancedSettings, + onOpenLabs = callback::navigateToLabs, onManageAccountClick = { onManageAccountClick(activity, it, isDark) }, - onOpenNotificationSettings = this::onOpenNotificationSettings, - onOpenLockScreenSettings = this::onOpenLockScreenSettings, - onOpenUserProfile = this::onOpenUserProfile, - onOpenBlockedUsers = this::onOpenBlockedUsers, + onOpenNotificationSettings = callback::navigateToNotificationSettings, + onOpenLockScreenSettings = callback::navigateToLockScreenSettings, + onOpenUserProfile = callback::navigateToUserProfile, + onOpenBlockedUsers = callback::navigateToBlockedUsers, onSignOutClick = { if (state.directLogoutState.canDoDirectSignOut) { state.directLogoutState.eventSink(DirectLogoutEvents.Logout(ignoreSdkError = false)) } else { - onSignOutClick() + callback.startSignOutFlow() } }, - onDeactivateClick = this::onOpenAccountDeactivation + onDeactivateClick = callback::startAccountDeactivationFlow ) directLogoutView.Render(state = state.directLogoutState) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt index c894aae05d..3aa9c61555 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/user/editprofile/EditUserProfilePresenter.kt @@ -143,7 +143,7 @@ class EditUserProfilePresenter( saveButtonEnabled = canSave && saveAction.value !is AsyncAction.Loading, saveAction = saveAction.value, cameraPermissionState = cameraPermissionState, - eventSink = { handleEvents(it) }, + eventSink = ::handleEvents, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt index 9e1bd70376..86ff65677c 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/DefaultPreferencesEntryPointTest.kt @@ -7,21 +7,19 @@ package io.element.android.features.preferences.impl -import android.content.Context import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.deactivation.api.AccountDeactivationEntryPoint -import io.element.android.features.licenses.api.OpenSourceLicensesEntryPoint -import io.element.android.features.lockscreen.api.LockScreenEntryPoint -import io.element.android.features.logout.api.LogoutEntryPoint +import io.element.android.features.deactivation.test.FakeAccountDeactivationEntryPoint +import io.element.android.features.licenses.test.FakeOpenSourceLicensesEntryPoint +import io.element.android.features.lockscreen.test.FakeLockScreenEntryPoint +import io.element.android.features.logout.test.FakeLogoutEntryPoint import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint -import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint +import io.element.android.libraries.troubleshoot.test.FakeNotificationTroubleShootEntryPoint +import io.element.android.libraries.troubleshoot.test.FakePushHistoryEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import org.junit.Rule @@ -41,41 +39,30 @@ class DefaultPreferencesEntryPointTest { PreferencesFlowNode( buildContext = buildContext, plugins = plugins, - lockScreenEntryPoint = object : LockScreenEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext, navTarget: LockScreenEntryPoint.Target) = lambdaError() - override fun pinUnlockIntent(context: Context) = lambdaError() - }, - notificationTroubleShootEntryPoint = object : NotificationTroubleShootEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - pushHistoryEntryPoint = object : PushHistoryEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - logoutEntryPoint = object : LogoutEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - openSourceLicensesEntryPoint = object : OpenSourceLicensesEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - accountDeactivationEntryPoint = object : AccountDeactivationEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + lockScreenEntryPoint = FakeLockScreenEntryPoint(), + notificationTroubleShootEntryPoint = FakeNotificationTroubleShootEntryPoint(), + pushHistoryEntryPoint = FakePushHistoryEntryPoint(), + logoutEntryPoint = FakeLogoutEntryPoint(), + openSourceLicensesEntryPoint = FakeOpenSourceLicensesEntryPoint(), + accountDeactivationEntryPoint = FakeAccountDeactivationEntryPoint(), ) } val callback = object : PreferencesEntryPoint.Callback { - override fun onAddAccount() = lambdaError() - override fun onOpenBugReport() = lambdaError() - override fun onSecureBackupClick() = lambdaError() - override fun onOpenRoomNotificationSettings(roomId: RoomId) = lambdaError() - override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError() + override fun navigateToAddAccount() = lambdaError() + override fun navigateToBugReport() = lambdaError() + override fun navigateToSecureBackup() = lambdaError() + override fun navigateToRoomNotificationSettings(roomId: RoomId) = lambdaError() + override fun navigateToEvent(roomId: RoomId, eventId: EventId) = lambdaError() } val params = PreferencesEntryPoint.Params( initialElement = PreferencesEntryPoint.InitialTarget.NotificationSettings, ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(PreferencesFlowNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/bugreport/BugReportEntryPoint.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/bugreport/BugReportEntryPoint.kt index 0eb84b529b..df6de4746e 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/bugreport/BugReportEntryPoint.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/bugreport/BugReportEntryPoint.kt @@ -13,12 +13,11 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface BugReportEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index b17d78f3aa..ddc1e8a198 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.features.rageshake.test) + testImplementation(projects.features.viewfolder.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.services.toolbox.test) testImplementation(libs.network.mockwebserver) diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt index 10af89f740..8749199fd1 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -25,6 +24,7 @@ import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint import io.element.android.features.viewfolder.api.ViewFolderEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import kotlinx.parcelize.Parcelize @@ -42,9 +42,7 @@ class BugReportFlowNode( buildContext = buildContext, plugins = plugins ) { - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: BugReportEntryPoint.Callback = callback() sealed interface NavTarget : Parcelable { @Parcelize @@ -61,10 +59,10 @@ class BugReportFlowNode( NavTarget.Root -> { val callback = object : BugReportNode.Callback { override fun onDone() { - this@BugReportFlowNode.onDone() + callback.onDone() } - override fun onViewLogs(basePath: String) { + override fun navigateToViewLogs(basePath: String) { backstack.push(NavTarget.ViewLogs(rootPath = basePath)) } } @@ -79,11 +77,12 @@ class BugReportFlowNode( val params = ViewFolderEntryPoint.Params( rootPath = navTarget.rootPath, ) - viewFolderEntryPoint - .nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + viewFolderEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } } } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt index e307dba8ec..b270f0cf3b 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportNode.kt @@ -13,13 +13,13 @@ 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.androidutils.system.toast +import io.element.android.libraries.architecture.callback import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(AppScope::class) @@ -32,16 +32,10 @@ class BugReportNode( ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { fun onDone() - fun onViewLogs(basePath: String) + fun navigateToViewLogs(basePath: String) } - private fun onViewLogs(basePath: String) { - plugins().forEach { it.onViewLogs(basePath) } - } - - private fun onDone() { - plugins().forEach { it.onDone() } - } + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { @@ -53,12 +47,12 @@ class BugReportNode( onBackClick = { navigateUp() }, onSuccess = { activity?.toast(CommonStrings.common_report_submitted) - onDone() + callback.onDone() }, onViewLogs = { // Force a logcat dump bugReporter.saveLogCat() - onViewLogs(bugReporter.logDirectory().absolutePath) + callback.navigateToViewLogs(bugReporter.logDirectory().absolutePath) } ) } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPoint.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPoint.kt index b4cfe28bc8..e094c62244 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPoint.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.rageshake.impl.bugreport import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint @@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultBugReportEntryPoint : BugReportEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): BugReportEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : BugReportEntryPoint.NodeBuilder { - override fun callback(callback: BugReportEntryPoint.Callback): BugReportEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: BugReportEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt index 89bec00ac5..7f173a8fe1 100755 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/reporter/DefaultBugReporter.kt @@ -165,7 +165,7 @@ class DefaultBugReporter( } } val sessionData = sessionStore.getLatestSession() - val numberOfAccounts = sessionStore.getAllSessions().size + val numberOfAccounts = sessionStore.numberOfSessions() val deviceId = sessionData?.deviceId ?: "undefined" val userId = sessionData?.userId?.let { UserId(it) } // build the multi part request diff --git a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPointTest.kt b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPointTest.kt index 23d74f7247..fb59100c43 100644 --- a/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPointTest.kt +++ b/features/rageshake/impl/src/test/kotlin/io/element/android/features/rageshake/impl/bugreport/DefaultBugReportEntryPointTest.kt @@ -9,11 +9,10 @@ package io.element.android.features.rageshake.impl.bugreport import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint -import io.element.android.features.viewfolder.api.ViewFolderEntryPoint +import io.element.android.features.viewfolder.test.FakeViewFolderEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -34,17 +33,17 @@ class DefaultBugReportEntryPointTest { BugReportFlowNode( buildContext = buildContext, plugins = plugins, - viewFolderEntryPoint = object : ViewFolderEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + viewFolderEntryPoint = FakeViewFolderEntryPoint(), ) } val callback = object : BugReportEntryPoint.Callback { override fun onDone() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(BugReportFlowNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/features/reportroom/api/src/main/kotlin/io/element/android/features/reportroom/api/ReportRoomEntryPoint.kt b/features/reportroom/api/src/main/kotlin/io/element/android/features/reportroom/api/ReportRoomEntryPoint.kt index 1eff7f8206..28536d9412 100644 --- a/features/reportroom/api/src/main/kotlin/io/element/android/features/reportroom/api/ReportRoomEntryPoint.kt +++ b/features/reportroom/api/src/main/kotlin/io/element/android/features/reportroom/api/ReportRoomEntryPoint.kt @@ -13,5 +13,9 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.RoomId fun interface ReportRoomEntryPoint : FeatureEntryPoint { - fun createNode(parentNode: Node, buildContext: BuildContext, roomId: RoomId): Node + fun createNode( + parentNode: Node, + buildContext: BuildContext, + roomId: RoomId, + ): Node } diff --git a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPoint.kt b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPoint.kt index d2f9eb8d7e..7babf80e39 100644 --- a/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPoint.kt +++ b/features/reportroom/impl/src/main/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPoint.kt @@ -17,7 +17,11 @@ import io.element.android.libraries.matrix.api.core.RoomId @ContributesBinding(AppScope::class) class DefaultReportRoomEntryPoint : ReportRoomEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, roomId: RoomId): Node { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + roomId: RoomId, + ): Node { return parentNode.createNode(buildContext, plugins = listOf(ReportRoomNode.Inputs(roomId))) } } diff --git a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPointTest.kt b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPointTest.kt index c9f850062e..3de8a6c926 100644 --- a/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPointTest.kt +++ b/features/reportroom/impl/src/test/kotlin/io/element/android/features/reportroom/impl/DefaultReportRoomEntryPointTest.kt @@ -32,7 +32,11 @@ class DefaultReportRoomEntryPointTest { } ) } - val result = entryPoint.createNode(parentNode, BuildContext.root(null), A_ROOM_ID) + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + roomId = A_ROOM_ID, + ) assertThat(result).isInstanceOf(ReportRoomNode::class.java) assertThat(result.plugins).contains(ReportRoomNode.Inputs(A_ROOM_ID)) } diff --git a/features/reportroom/test/build.gradle.kts b/features/reportroom/test/build.gradle.kts new file mode 100644 index 0000000000..42528ad531 --- /dev/null +++ b/features/reportroom/test/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.reportroom.test" +} + +dependencies { + implementation(projects.features.reportroom.api) + implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) + implementation(projects.tests.testutils) +} diff --git a/features/reportroom/test/src/main/kotlin/io/element/android/features/reportroom/test/FakeReportRoomEntryPoint.kt b/features/reportroom/test/src/main/kotlin/io/element/android/features/reportroom/test/FakeReportRoomEntryPoint.kt new file mode 100644 index 0000000000..0ff0a3ed89 --- /dev/null +++ b/features/reportroom/test/src/main/kotlin/io/element/android/features/reportroom/test/FakeReportRoomEntryPoint.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.reportroom.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.reportroom.api.ReportRoomEntryPoint +import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeReportRoomEntryPoint : ReportRoomEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + roomId: RoomId, + ): Node { + lambdaError() + } +} diff --git a/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt index 420bcfa97c..1bdde018e0 100644 --- a/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt +++ b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt @@ -16,13 +16,12 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias interface RoomAliasResolverEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun params(params: Params): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onAliasResolved(data: ResolvedRoomAlias) diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt index df653437f1..b6e4fe6716 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.roomaliasresolver.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint @@ -17,23 +16,15 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultRoomAliasResolverEntryPoint : RoomAliasResolverEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomAliasResolverEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : RoomAliasResolverEntryPoint.NodeBuilder { - override fun callback(callback: RoomAliasResolverEntryPoint.Callback): RoomAliasResolverEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun params(params: RoomAliasResolverEntryPoint.Params): RoomAliasResolverEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: RoomAliasResolverEntryPoint.Params, + callback: RoomAliasResolverEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback), + ) } } diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt index d7b3242def..656017b484 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt @@ -12,14 +12,13 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @ContributesNode(SessionScope::class) @AssistedInject @@ -28,22 +27,19 @@ class RoomAliasResolverNode( @Assisted plugins: List, presenterFactory: RoomAliasResolverPresenter.Factory, ) : Node(buildContext, plugins = plugins) { + private val callback: RoomAliasResolverEntryPoint.Callback = callback() private val inputs = inputs() private val presenter = presenterFactory.create( inputs.roomAlias ) - private fun onAliasResolved(data: ResolvedRoomAlias) { - plugins().forEach { it.onAliasResolved(data) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomAliasResolverView( state = state, - onSuccess = ::onAliasResolved, + onSuccess = callback::onAliasResolved, onBackClick = ::navigateUp, modifier = modifier ) diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt index 238b35017b..6e3dbb2ba8 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt @@ -43,10 +43,12 @@ class DefaultRoomAliasResolverEntryPointTest { val params = RoomAliasResolverEntryPoint.Params( roomAlias = A_ROOM_ALIAS ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(RoomAliasResolverNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index 48fcac8ee6..96ff1ae96e 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -37,17 +37,16 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint { data class Params(val initialElement: InitialTarget) : NodeInputs interface Callback : Plugin { - fun onOpenGlobalNotificationSettings() - fun onOpenRoom(roomId: RoomId, serverNames: List) - fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) - fun forwardEvent(eventId: EventId) + fun navigateToGlobalNotificationSettings() + fun navigateToRoom(roomId: RoomId, serverNames: List) + fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) + fun startForwardEventFlow(eventId: EventId, fromPinnedEvents: Boolean) } - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } - - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node } diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 882058ef48..896fc8dbdc 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -63,10 +63,18 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.mediapickers.test) + testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.libraries.featureflag.test) + testImplementation(projects.features.call.test) + testImplementation(projects.features.changeroommemberroles.test) + testImplementation(projects.features.knockrequests.test) + testImplementation(projects.features.messages.test) + testImplementation(projects.features.poll.test) + testImplementation(projects.features.reportroom.test) testImplementation(projects.features.startchat.test) + testImplementation(projects.features.verifysession.test) testImplementation(projects.services.analytics.test) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt index 5679b8c361..8066cc70f5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.roomdetails.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint @@ -19,24 +18,16 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultRoomDetailsEntryPoint : RoomDetailsEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDetailsEntryPoint.NodeBuilder { - return object : RoomDetailsEntryPoint.NodeBuilder { - val plugins = ArrayList() - - override fun params(params: RoomDetailsEntryPoint.Params): RoomDetailsEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun callback(callback: RoomDetailsEntryPoint.Callback): RoomDetailsEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: RoomDetailsEntryPoint.Params, + callback: RoomDetailsEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback) + ) } } 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 b0540626b9..5eee694786 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 @@ -16,7 +16,6 @@ import androidx.lifecycle.coroutineScope 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -27,8 +26,8 @@ import io.element.android.annotations.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.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.poll.api.history.PollHistoryEntryPoint @@ -45,6 +44,7 @@ import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback 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 @@ -146,6 +146,8 @@ class RoomDetailsFlowNode( data object SelectNewOwnersWhenLeaving : NavTarget } + private val callback: RoomDetailsEntryPoint.Callback = callback() + override fun onBuilt() { super.onBuilt() whenChildrenAttached { @@ -167,55 +169,55 @@ class RoomDetailsFlowNode( return when (navTarget) { NavTarget.RoomDetails -> { val roomDetailsCallback = object : RoomDetailsNode.Callback { - override fun openRoomMemberList() { + override fun navigateToRoomMemberList() { backstack.push(NavTarget.RoomMemberList) } - override fun editRoomDetails() { + override fun navigateToRoomDetailsEdit() { backstack.push(NavTarget.RoomDetailsEdit) } - override fun openInviteMembers() { + override fun navigateToInviteMembers() { backstack.push(NavTarget.InviteMembers) } - override fun openRoomNotificationSettings() { + override fun navigateToRoomNotificationSettings() { backstack.push(NavTarget.RoomNotificationSettings(showUserDefinedSettingStyle = false)) } - override fun openAvatarPreview(name: String, url: String) { + override fun navigateToAvatarPreview(name: String, url: String) { overlay.show(NavTarget.AvatarPreview(name, url)) } - override fun openPollHistory() { + override fun navigateToPollHistory() { backstack.push(NavTarget.PollHistory) } - override fun openMediaGallery() { + override fun navigateToMediaGallery() { backstack.push(NavTarget.MediaGallery) } - override fun openAdminSettings() { + override fun navigateToAdminSettings() { backstack.push(NavTarget.AdminSettings) } - override fun openPinnedMessagesList() { + override fun navigateToPinnedMessagesList() { backstack.push(NavTarget.PinnedMessagesList) } - override fun openKnockRequestsList() { + override fun navigateToKnockRequestsList() { backstack.push(NavTarget.KnockRequestsList) } - override fun openSecurityAndPrivacy() { + override fun navigateToSecurityAndPrivacy() { backstack.push(NavTarget.SecurityAndPrivacy) } - override fun openDmUserProfile(userId: UserId) { + override fun navigateToRoomMemberDetails(userId: UserId) { backstack.push(NavTarget.RoomMemberDetails(userId)) } - override fun onJoinCall() { + override fun navigateToRoomCall() { val inputs = CallType.RoomCall( sessionId = room.sessionId, roomId = room.roomId, @@ -224,11 +226,11 @@ class RoomDetailsFlowNode( elementCallEntryPoint.startCall(inputs) } - override fun openReportRoom() { + override fun navigateToReportRoom() { backstack.push(NavTarget.ReportRoom) } - override fun onSelectNewOwnersWhenLeaving() { + override fun navigateToSelectNewOwnersWhenLeaving() { backstack.push(NavTarget.SelectNewOwnersWhenLeaving) } } @@ -237,11 +239,11 @@ class RoomDetailsFlowNode( NavTarget.RoomMemberList -> { val roomMemberListCallback = object : RoomMemberListNode.Callback { - override fun openRoomMemberDetails(roomMemberId: UserId) { + override fun navigateToRoomMemberDetails(roomMemberId: UserId) { backstack.push(NavTarget.RoomMemberDetails(roomMemberId)) } - override fun openInviteMembers() { + override fun navigateToInviteMembers() { backstack.push(NavTarget.InviteMembers) } } @@ -259,8 +261,8 @@ class RoomDetailsFlowNode( is NavTarget.RoomNotificationSettings -> { val input = RoomNotificationSettingsNode.RoomNotificationSettingInput(navTarget.showUserDefinedSettingStyle) val callback = object : RoomNotificationSettingsNode.Callback { - override fun openGlobalNotificationSettings() { - plugins().forEach { it.onOpenGlobalNotificationSettings() } + override fun navigateToGlobalNotificationSettings() { + callback.navigateToGlobalNotificationSettings() } } createNode(buildContext, listOf(input, callback)) @@ -268,19 +270,19 @@ class RoomDetailsFlowNode( is NavTarget.RoomMemberDetails -> { val callback = object : UserProfileNodeHelper.Callback { - override fun openAvatarPreview(username: String, avatarUrl: String) { + override fun navigateToAvatarPreview(username: String, avatarUrl: String) { overlay.show(NavTarget.AvatarPreview(username, avatarUrl)) } - override fun onStartDM(roomId: RoomId) { - plugins().forEach { it.onOpenRoom(roomId, emptyList()) } + override fun navigateToRoom(roomId: RoomId) { + callback.navigateToRoom(roomId, emptyList()) } - override fun onStartCall(dmRoomId: RoomId) { + override fun startCall(dmRoomId: RoomId) { elementCallEntryPoint.startCall(CallType.RoomCall(roomId = dmRoomId, sessionId = room.sessionId)) } - override fun onVerifyUser(userId: UserId) { + override fun startVerifyUserFlow(userId: UserId) { backstack.push(NavTarget.VerifyUser(userId)) } } @@ -293,21 +295,24 @@ class RoomDetailsFlowNode( overlay.hide() } - override fun onViewInTimeline(eventId: EventId) { + override fun viewInTimeline(eventId: EventId) { // Cannot happen } - override fun onForwardEvent(eventId: EventId) { + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { // Cannot happen } } - mediaViewerEntryPoint.nodeBuilder(this, buildContext) - .avatar( - navTarget.name, - navTarget.avatarUrl, - ) - .callback(callback) - .build() + val params = mediaViewerEntryPoint.createParamsForAvatar( + filename = navTarget.name, + avatarUrl = navTarget.avatarUrl, + ) + mediaViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } is NavTarget.PollHistory -> { pollHistoryEntryPoint.createNode(this, buildContext) @@ -318,23 +323,23 @@ class RoomDetailsFlowNode( backstack.pop() } - override fun onViewInTimeline(eventId: EventId) { + override fun viewInTimeline(eventId: EventId) { val permalinkData = PermalinkData.RoomLink( roomIdOrAlias = room.roomId.toRoomIdOrAlias(), eventId = eventId, ) - plugins().forEach { - it.onPermalinkClick(permalinkData, pushToBackstack = false) - } + callback.handlePermalinkClick(permalinkData, pushToBackstack = false) } - override fun forwardEvent(eventId: EventId) { - plugins().forEach { it.forwardEvent(eventId) } + override fun forward(eventId: EventId, fromPinnedEvents: Boolean) { + callback.startForwardEventFlow(eventId, fromPinnedEvents) } } - mediaGalleryEntryPoint.nodeBuilder(this, buildContext) - .callback(callback) - .build() + mediaGalleryEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } is NavTarget.AdminSettings -> { @@ -345,26 +350,28 @@ class RoomDetailsFlowNode( MessagesEntryPoint.InitialTarget.PinnedMessages ) val callback = object : MessagesEntryPoint.Callback { - override fun onRoomDetailsClick() = Unit + override fun navigateToRoomDetails() = Unit - override fun onUserDataClick(userId: UserId) = Unit + override fun navigateToRoomMemberDetails(userId: UserId) = Unit - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { - plugins().forEach { it.onPermalinkClick(data, pushToBackstack) } + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { + callback.handlePermalinkClick(data, pushToBackstack) } - override fun forwardEvent(eventId: EventId) { - plugins().forEach { it.forwardEvent(eventId) } + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { + callback.startForwardEventFlow(eventId, fromPinnedEvents) } - override fun openRoom(roomId: RoomId) { - plugins().forEach { it.onOpenRoom(roomId, emptyList()) } + override fun navigateToRoom(roomId: RoomId) { + callback.navigateToRoom(roomId, emptyList()) } } - return messagesEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(callback) - .build() + return messagesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } NavTarget.KnockRequestsList -> { knockRequestsListEntryPoint.createNode(this, buildContext) @@ -377,9 +384,11 @@ class RoomDetailsFlowNode( showDeviceVerifiedScreen = true, verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId) ) - outgoingVerificationEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .callback(object : OutgoingVerificationEntryPoint.Callback { + outgoingVerificationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = object : OutgoingVerificationEntryPoint.Callback { override fun onDone() { backstack.pop() } @@ -388,21 +397,27 @@ class RoomDetailsFlowNode( backstack.pop() } - override fun onLearnMoreAboutEncryption() { + override fun navigateToLearnMoreAboutEncryption() { learnMoreUrl.value = LearnMoreConfig.ENCRYPTION_URL } - }) - .build() + }, + ) } is NavTarget.ReportRoom -> { - reportRoomEntryPoint.createNode(this, buildContext, room.roomId) + reportRoomEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + roomId = room.roomId, + ) } is NavTarget.SelectNewOwnersWhenLeaving -> { - changeRoomMemberRolesEntryPoint.builder(this, buildContext) - .room(room) - .listType(ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving) - .build() + changeRoomMemberRolesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + room = room, + listType = ChangeRoomMemberRolesListType.SelectNewOwnersWhenLeaving, + ) } } } 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 2ada71dbc8..4af3d454d9 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 @@ -18,7 +18,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen @@ -26,6 +25,7 @@ import io.element.android.annotations.ContributesNode import io.element.android.features.leaveroom.api.LeaveRoomRenderer import io.element.android.libraries.androidutils.system.startSharePlainTextIntent import io.element.android.libraries.architecture.appyx.launchMolecule +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.BaseRoom @@ -46,24 +46,24 @@ class RoomDetailsNode( private val leaveRoomRenderer: LeaveRoomRenderer, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun openRoomMemberList() - fun openInviteMembers() - fun editRoomDetails() - fun openRoomNotificationSettings() - fun openAvatarPreview(name: String, url: String) - fun openPollHistory() - fun openMediaGallery() - fun openAdminSettings() - fun openPinnedMessagesList() - fun openKnockRequestsList() - fun openSecurityAndPrivacy() - fun openDmUserProfile(userId: UserId) - fun onJoinCall() - fun openReportRoom() - fun onSelectNewOwnersWhenLeaving() + fun navigateToRoomMemberList() + fun navigateToInviteMembers() + fun navigateToRoomDetailsEdit() + fun navigateToRoomNotificationSettings() + fun navigateToAvatarPreview(name: String, url: String) + fun navigateToPollHistory() + fun navigateToMediaGallery() + fun navigateToAdminSettings() + fun navigateToPinnedMessagesList() + fun navigateToKnockRequestsList() + fun navigateToSecurityAndPrivacy() + fun navigateToRoomMemberDetails(userId: UserId) + fun navigateToRoomCall() + fun navigateToReportRoom() + fun navigateToSelectNewOwnersWhenLeaving() } - private val callback = plugins().first() + private val callback: Callback = callback() init { lifecycle.subscribe( @@ -73,30 +73,6 @@ class RoomDetailsNode( ) } - private fun openRoomMemberList() { - callback.openRoomMemberList() - } - - private fun openRoomNotificationSettings() { - callback.openRoomNotificationSettings() - } - - private fun invitePeople() { - callback.openInviteMembers() - } - - private fun openPollHistory() { - callback.openPollHistory() - } - - private fun openMediaGallery() { - callback.openMediaGallery() - } - - private fun onJoinCall() { - callback.onJoinCall() - } - private fun CoroutineScope.onShareRoom(context: Context) = launch { room.getPermalink() .onSuccess { permalink -> @@ -112,42 +88,6 @@ class RoomDetailsNode( } } - private fun onEditRoomDetails() { - callback.editRoomDetails() - } - - private fun openAvatarPreview(name: String, url: String) { - callback.openAvatarPreview(name, url) - } - - private fun openAdminSettings() { - callback.openAdminSettings() - } - - private fun openPinnedMessages() { - callback.openPinnedMessagesList() - } - - private fun openKnockRequestsLists() { - callback.openKnockRequestsList() - } - - private fun openSecurityAndPrivacy() { - callback.openSecurityAndPrivacy() - } - - private fun onProfileClick(userId: UserId) { - callback.openDmUserProfile(userId) - } - - private fun onReportRoomClick() { - callback.openReportRoom() - } - - private fun onSelectNewOwnersWhenLeaving() { - return callback.onSelectNewOwnersWhenLeaving() - } - private val stateFlow = launchMolecule { presenter.present() } fun onNewOwnersSelected() { @@ -165,34 +105,38 @@ class RoomDetailsNode( fun onActionClick(action: RoomDetailsAction) { when (action) { - RoomDetailsAction.Edit -> onEditRoomDetails() - RoomDetailsAction.AddTopic -> onEditRoomDetails() + RoomDetailsAction.Edit -> { + callback.navigateToRoomDetailsEdit() + } + RoomDetailsAction.AddTopic -> { + callback.navigateToRoomDetailsEdit() + } } } RoomDetailsView( state = state, modifier = modifier, - goBack = this::navigateUp, + goBack = ::navigateUp, onActionClick = ::onActionClick, onShareRoom = ::onShareRoom, - openRoomMemberList = ::openRoomMemberList, - openRoomNotificationSettings = ::openRoomNotificationSettings, - invitePeople = ::invitePeople, - openAvatarPreview = ::openAvatarPreview, - openPollHistory = ::openPollHistory, - openMediaGallery = ::openMediaGallery, - openAdminSettings = this::openAdminSettings, - onJoinCallClick = ::onJoinCall, - onPinnedMessagesClick = ::openPinnedMessages, - onKnockRequestsClick = ::openKnockRequestsLists, - onSecurityAndPrivacyClick = ::openSecurityAndPrivacy, - onProfileClick = ::onProfileClick, - onReportRoomClick = ::onReportRoomClick, + openRoomMemberList = callback::navigateToRoomMemberList, + openRoomNotificationSettings = callback::navigateToRoomNotificationSettings, + invitePeople = callback::navigateToInviteMembers, + openAvatarPreview = callback::navigateToAvatarPreview, + openPollHistory = callback::navigateToPollHistory, + openMediaGallery = callback::navigateToMediaGallery, + openAdminSettings = callback::navigateToAdminSettings, + onJoinCallClick = callback::navigateToRoomCall, + onPinnedMessagesClick = callback::navigateToPinnedMessagesList, + onKnockRequestsClick = callback::navigateToKnockRequestsList, + onSecurityAndPrivacyClick = callback::navigateToSecurityAndPrivacy, + onProfileClick = callback::navigateToRoomMemberDetails, + onReportRoomClick = callback::navigateToReportRoom, leaveRoomView = { leaveRoomRenderer.Render( state = state.leaveRoomState, - onSelectNewOwners = { onSelectNewOwnersWhenLeaving() }, + onSelectNewOwners = { callback.navigateToSelectNewOwnersWhenLeaving() }, modifier = Modifier ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt index cc7a6e2151..092326928a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListNode.kt @@ -13,7 +13,6 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen @@ -21,6 +20,7 @@ import io.element.android.annotations.ContributesNode import io.element.android.features.roommembermoderation.api.ModerationAction import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.UserId import io.element.android.services.analytics.api.AnalyticsService @@ -35,11 +35,11 @@ class RoomMemberListNode( private val roomMemberModerationRenderer: RoomMemberModerationRenderer, ) : Node(buildContext, plugins = plugins), RoomMemberListNavigator { interface Callback : Plugin { - fun openRoomMemberDetails(roomMemberId: UserId) - fun openInviteMembers() + fun navigateToRoomMemberDetails(roomMemberId: UserId) + fun navigateToInviteMembers() } - private val callbacks = plugins() + private val callback: Callback = callback() init { lifecycle.subscribe( @@ -50,15 +50,11 @@ class RoomMemberListNode( } override fun openRoomMemberDetails(roomMemberId: UserId) { - callbacks.forEach { - it.openRoomMemberDetails(roomMemberId) - } + callback.navigateToRoomMemberDetails(roomMemberId) } override fun openInviteMembers() { - callbacks.forEach { - it.openInviteMembers() - } + callback.navigateToInviteMembers() } override fun exitRoomMemberList() { 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 69efd04b9e..90619d2e2d 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 @@ -179,7 +179,7 @@ class RoomMemberListPresenter( isSearchActive = isSearchActive, canInvite = canInvite, moderationState = roomModerationState, - eventSink = { handleEvents(it) }, + eventSink = ::handleEvents, ) } 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 3b364c6f92..52aa7924e6 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 @@ -62,12 +62,12 @@ class RoomMemberDetailsNode( userProfileNodeHelper.onShareUser(context, permalinkBuilder) } - fun onStartDM(roomId: RoomId) { - callback.onStartDM(roomId) + fun navigateToRoom(roomId: RoomId) { + callback.navigateToRoom(roomId) } fun onStartCall(roomId: RoomId) { - callback.onStartCall(roomId) + callback.startCall(roomId) } val state = presenter.present() @@ -77,10 +77,10 @@ class RoomMemberDetailsNode( modifier = modifier, goBack = this::navigateUp, onShareUser = ::onShareUser, - onOpenDm = ::onStartDM, + onOpenDm = ::navigateToRoom, onStartCall = ::onStartCall, - openAvatarPreview = callback::openAvatarPreview, - onVerifyClick = callback::onVerifyUser, + openAvatarPreview = callback::navigateToAvatarPreview, + onVerifyClick = callback::startVerifyUserFlow, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt index 0e5a9b23b1..ab4b288803 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsNode.kt @@ -13,12 +13,12 @@ import com.bumble.appyx.core.lifecycle.subscribe import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.services.analytics.api.AnalyticsService @@ -34,17 +34,16 @@ class RoomNotificationSettingsNode( data class RoomNotificationSettingInput( val showUserDefinedSettingStyle: Boolean ) : NodeInputs - interface Callback : Plugin { - fun openGlobalNotificationSettings() - } - private val inputs = inputs() - private val callbacks = plugins() - private fun openGlobalNotificationSettings() { - callbacks.forEach { it.openGlobalNotificationSettings() } + interface Callback : Plugin { + fun navigateToGlobalNotificationSettings() } + private val callback: Callback = callback() + private val inputs = inputs() + private val presenter = presenterFactory.create(inputs.showUserDefinedSettingStyle) + init { lifecycle.subscribe( onResume = { @@ -59,8 +58,8 @@ class RoomNotificationSettingsNode( RoomNotificationSettingsView( state = state, modifier = modifier, - onShowGlobalNotifications = this::openGlobalNotificationSettings, - onBackClick = this::navigateUp, + onShowGlobalNotifications = callback::navigateToGlobalNotificationSettings, + onBackClick = ::navigateUp, ) } } 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 413e71169a..7bfdf9949c 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 @@ -135,7 +135,7 @@ class RoomNotificationSettingsPresenter( setNotificationSettingAction = setNotificationSettingAction.value, restoreDefaultAction = restoreDefaultAction.value, displayMentionsOnlyDisclaimer = shouldDisplayMentionsOnlyDisclaimer, - eventSink = { handleEvents(it) }, + eventSink = ::handleEvents, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt index 62689489eb..b2b7d46780 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsFlowNode.kt @@ -20,8 +20,8 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesEntryPoint +import io.element.android.features.changeroommemberroles.api.ChangeRoomMemberRolesListType import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsNode import io.element.android.features.roomdetails.impl.rolesandpermissions.permissions.ChangeRoomPermissionsSection import io.element.android.libraries.architecture.BackstackView @@ -101,16 +101,20 @@ class RolesAndPermissionsFlowNode( ) } is NavTarget.AdminList -> { - changeRoomMemberRolesEntryPoint.builder(this, buildContext) - .room(joinedRoom) - .listType(ChangeRoomMemberRolesListType.Admins) - .build() + changeRoomMemberRolesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + room = joinedRoom, + listType = ChangeRoomMemberRolesListType.Admins, + ) } is NavTarget.ModeratorList -> { - changeRoomMemberRolesEntryPoint.builder(this, buildContext) - .room(joinedRoom) - .listType(ChangeRoomMemberRolesListType.Moderators) - .build() + changeRoomMemberRolesEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + room = joinedRoom, + listType = ChangeRoomMemberRolesListType.Moderators, + ) } is NavTarget.ChangeRoomPermissions -> { val inputs = ChangeRoomPermissionsNode.Inputs(navTarget.section) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt index a430b0f6a5..29394398d3 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt @@ -14,10 +14,10 @@ import androidx.lifecycle.lifecycleScope import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.RoomMember @@ -45,7 +45,7 @@ class RolesAndPermissionsNode( override fun onBackClick() {} } - private val callback = plugins().first() + private val callback: Callback = callback() @Stable private val navigator = object : RolesAndPermissionsNavigator by callback { 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 2ad4c84028..e7033d37be 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 @@ -100,7 +100,7 @@ class RolesAndPermissionsPresenter( canDemoteSelf = canDemoteSelf.value, changeOwnRoleAction = changeOwnRoleAction.value, resetPermissionsAction = resetPermissionsAction.value, - eventSink = { handleEvent(it) }, + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt index ec90df1d5e..e9636978d5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsPresenter.kt @@ -112,7 +112,7 @@ class ChangeRoomPermissionsPresenter( hasChanges = hasChanges, saveAction = saveAction, confirmExitAction = confirmExitAction, - eventSink = { handleEvent(it) } + eventSink = ::handleEvent, ) } diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index 4a4f8a44b6..f88c017970 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -70,7 +70,7 @@ "Informationen" "Thema" "Chat wird aktualisiert…" - "In diesem Chat gibt es keine gesperrten Nutzer." + "Es gibt keine gesperrten Nutzer." "%1$d Person" "%1$d Personen" diff --git a/features/roomdetails/impl/src/main/res/values-et/translations.xml b/features/roomdetails/impl/src/main/res/values-et/translations.xml index 6de3107def..a8f927e675 100644 --- a/features/roomdetails/impl/src/main/res/values-et/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-et/translations.xml @@ -70,7 +70,7 @@ "Jututoa teave" "Teema" "Uuendame jututuba…" - "Jututoas pole suhtluskeeluga kasutajaid" + "Suhtluskeeluga kasutajaid pole" "%1$d osaleja" "%1$d osalejat" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index ff3dc18103..fa6fa06957 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -70,7 +70,7 @@ "Informácie o miestnosti" "Téma" "Aktualizácia miestnosti…" - "V tejto miestnosti nie sú žiadni zakázaní používatelia." + "Neexistujú žiadni zablokovaní používatelia." "%1$d osoba" "%1$d osoby" 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 888701406d..b1773df979 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 @@ -70,7 +70,7 @@ "聊天室資訊" "主題" "正在更新聊天室…" - "此聊天室沒有黑名單。" + "沒有被封鎖的使用者。" "%1$d 位夥伴" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index ce8eb3a7b7..814f352abd 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -70,7 +70,7 @@ "Room info" "Topic" "Updating room…" - "There are no banned users in this room." + "There are no banned users." "%1$d person" "%1$d people" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index a9d528ace5..f9bc40b711 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -9,26 +9,23 @@ package io.element.android.features.roomdetails.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType -import io.element.android.features.call.api.ElementCallEntryPoint -import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint -import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint -import io.element.android.features.messages.api.MessagesEntryPoint -import io.element.android.features.poll.api.history.PollHistoryEntryPoint -import io.element.android.features.reportroom.api.ReportRoomEntryPoint +import io.element.android.features.call.test.FakeElementCallEntryPoint +import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint +import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint +import io.element.android.features.messages.test.FakeMessagesEntryPoint +import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint +import io.element.android.features.reportroom.test.FakeReportRoomEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint -import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint +import io.element.android.features.verifysession.test.FakeOutgoingVerificationEntryPoint 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.permalink.PermalinkData import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.room.FakeJoinedRoom -import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint -import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.libraries.mediaviewer.test.FakeMediaGalleryEntryPoint +import io.element.android.libraries.mediaviewer.test.FakeMediaViewerEntryPoint import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode @@ -50,62 +47,34 @@ class DefaultRoomDetailsEntryPointTest { RoomDetailsFlowNode( buildContext = buildContext, plugins = plugins, - pollHistoryEntryPoint = object : PollHistoryEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - elementCallEntryPoint = object : ElementCallEntryPoint { - override fun startCall(callType: CallType) = lambdaError() - override suspend fun handleIncomingCall( - callType: CallType.RoomCall, - eventId: EventId, - senderId: UserId, - roomName: String?, - senderName: String?, - avatarUrl: String?, - timestamp: Long, - expirationTimestamp: Long, - notificationChannelId: String, - textContent: String? - ) = lambdaError() - }, + pollHistoryEntryPoint = FakePollHistoryEntryPoint(), + elementCallEntryPoint = FakeElementCallEntryPoint(), room = FakeJoinedRoom(), analyticsService = FakeAnalyticsService(), - messagesEntryPoint = object : MessagesEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - knockRequestsListEntryPoint = object : KnockRequestsListEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - mediaViewerEntryPoint = object : MediaViewerEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - mediaGalleryEntryPoint = object : MediaGalleryEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - outgoingVerificationEntryPoint = object : OutgoingVerificationEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - reportRoomEntryPoint = object : ReportRoomEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, roomId: RoomId) = lambdaError() - }, - changeRoomMemberRolesEntryPoint = object : ChangeRoomMemberRolesEntryPoint { - override fun builder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + messagesEntryPoint = FakeMessagesEntryPoint(), + knockRequestsListEntryPoint = FakeKnockRequestsListEntryPoint(), + mediaViewerEntryPoint = FakeMediaViewerEntryPoint(), + mediaGalleryEntryPoint = FakeMediaGalleryEntryPoint(), + outgoingVerificationEntryPoint = FakeOutgoingVerificationEntryPoint(), + reportRoomEntryPoint = FakeReportRoomEntryPoint(), + changeRoomMemberRolesEntryPoint = FakeChangeRoomMemberRolesEntryPoint(), ) } val callback = object : RoomDetailsEntryPoint.Callback { - override fun onOpenGlobalNotificationSettings() = lambdaError() - override fun onOpenRoom(roomId: RoomId, serverNames: List) = lambdaError() - override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() - override fun forwardEvent(eventId: EventId) = lambdaError() + override fun navigateToGlobalNotificationSettings() = lambdaError() + override fun navigateToRoom(roomId: RoomId, serverNames: List) = lambdaError() + override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) = lambdaError() + override fun startForwardEventFlow(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() } val params = RoomDetailsEntryPoint.Params( initialElement = RoomDetailsEntryPoint.InitialTarget.RoomDetails, ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(RoomDetailsFlowNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt index 892719554b..c3cb8a199b 100644 --- a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt +++ b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDirectoryEntryPoint.kt @@ -13,14 +13,13 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface RoomDirectoryEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { - fun onResultClick(roomDescription: RoomDescription) + fun navigateToRoom(roomDescription: RoomDescription) } } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt index 6716bbbf97..48211d7f59 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.roomdirectory.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint @@ -18,18 +17,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultRoomDirectoryEntryPoint : RoomDirectoryEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomDirectoryEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : RoomDirectoryEntryPoint.NodeBuilder { - override fun callback(callback: RoomDirectoryEntryPoint.Callback): RoomDirectoryEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: RoomDirectoryEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt index 03d2be6e35..ec01ea3ba6 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryNode.kt @@ -12,12 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -27,18 +26,14 @@ class RoomDirectoryNode( @Assisted plugins: List, private val presenter: RoomDirectoryPresenter, ) : Node(buildContext, plugins = plugins) { - private fun onResultClick(roomDescription: RoomDescription) { - plugins().forEach { - it.onResultClick(roomDescription) - } - } + private val callback: RoomDirectoryEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomDirectoryView( state = state, - onResultClick = ::onResultClick, + onResultClick = callback::navigateToRoom, onBackClick = ::navigateUp, modifier = modifier ) diff --git a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPointTest.kt b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPointTest.kt index d544f55000..24205d6681 100644 --- a/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPointTest.kt +++ b/features/roomdirectory/impl/src/test/kotlin/io/element/android/features/roomdirectory/impl/DefaultRoomDirectoryEntryPointTest.kt @@ -35,11 +35,13 @@ class DefaultRoomDirectoryEntryPointTest { ) } val callback = object : RoomDirectoryEntryPoint.Callback { - override fun onResultClick(roomDescription: RoomDescription) = lambdaError() + override fun navigateToRoom(roomDescription: RoomDescription) = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(RoomDirectoryNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt index 2ddb3ad34a..995baa069b 100644 --- a/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt +++ b/features/roommembermoderation/impl/src/main/kotlin/io/element/android/features/roommembermoderation/impl/RoomMemberModerationPresenter.kt @@ -140,7 +140,7 @@ class RoomMemberModerationPresenter( kickUserAsyncAction = kickUserAsyncAction.value, banUserAsyncAction = banUserAsyncAction.value, unbanUserAsyncAction = unbanUserAsyncAction.value, - eventSink = { handleEvent(it) }, + eventSink = ::handleEvent, ) } diff --git a/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml b/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml index 263aa9a2e0..20fc68c391 100644 --- a/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-cs/translations.xml @@ -9,7 +9,7 @@ "Pokud budou pozváni, budou se moci do této místnosti znovu připojit." "Opravdu chcete tohoto člena odebrat?" "Zobrazit profil" - "Odebrat z místnosti" + "Odebrat uživatele" "Odebrat člena a zakázat mu připojení v budoucnu?" "Odstraňování %1$s…" "Zrušit vykázání z místnosti" diff --git a/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml b/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml index ffdd634b0b..c852e7ab29 100644 --- a/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml +++ b/features/roommembermoderation/impl/src/main/res/values-sk/translations.xml @@ -4,12 +4,14 @@ "Zakázať" "Nebudú sa môcť pripojiť k tejto miestnosti znova ani ak budú pozvaní." "Ste si istý, že chcete zakázať tohto člena?" + "Ak dostanú pozvánku, nebudú sa môcť k tomuto priestoru znova pripojiť, ale stále si ponechajú členstvo vo všetkých miestnostiach alebo podpriestoroch." "Zakazuje sa %1$s" "Odstrániť" "V prípade pozvania sa budú môcť znova pripojiť k tejto miestnosti." "Ste si istý, že chcete odstrániť tohto člena?" + "Ak dostanú pozvánku, budú sa môcť k tomuto priestoru znova pripojiť a stále si ponechajú členstvo vo všetkých miestnostiach alebo podpriestoroch." "Zobraziť profil" - "Odstrániť z miestnosti" + "Odstrániť používateľa" "Odstrániť člena a zakázať vstup v budúcnosti?" "Odstraňuje sa %1$s…" "Zrušiť zákaz prístupu do miestnosti" diff --git a/features/roommembermoderation/impl/src/main/res/values/localazy.xml b/features/roommembermoderation/impl/src/main/res/values/localazy.xml index e3f6071898..3d23c8763a 100644 --- a/features/roommembermoderation/impl/src/main/res/values/localazy.xml +++ b/features/roommembermoderation/impl/src/main/res/values/localazy.xml @@ -4,10 +4,12 @@ "Ban" "They won’t be able to join again if invited." "Are you sure you want to ban this member?" + "They won’t be able to join this space again if invited, but they’ll still keep their memberships of any rooms or subspaces." "Banning %1$s" "Remove" "They will be able to join this room again if invited." "Are you sure you want to remove this member?" + "They will be able to join this space again if invited, and they’ll still keep their memberships of any rooms or subspaces." "View profile" "Remove user" "Remove member and ban from joining in the future?" diff --git a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt index 4fb2ab00cc..cb1af18f24 100644 --- a/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt +++ b/features/securebackup/api/src/main/kotlin/io/element/android/features/securebackup/api/SecureBackupEntryPoint.kt @@ -32,15 +32,14 @@ interface SecureBackupEntryPoint : FeatureEntryPoint { data class Params(val initialElement: InitialTarget) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() } - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPoint.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPoint.kt index 9e8138ca8c..b2c533b53b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPoint.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.securebackup.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.securebackup.api.SecureBackupEntryPoint @@ -17,23 +16,15 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultSecureBackupEntryPoint : SecureBackupEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SecureBackupEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : SecureBackupEntryPoint.NodeBuilder { - override fun params(params: SecureBackupEntryPoint.Params): SecureBackupEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun callback(callback: SecureBackupEntryPoint.Callback): SecureBackupEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: SecureBackupEntryPoint.Params, + callback: SecureBackupEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback) + ) } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt index 4d79f8ea1b..586f37b664 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/SecureBackupFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -29,6 +28,7 @@ import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.appyx.canPop +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import kotlinx.parcelize.Parcelize @@ -71,25 +71,25 @@ class SecureBackupFlowNode( data object ResetIdentity : NavTarget } - private val callbacks = plugins() + private val callback: SecureBackupEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : SecureBackupRootNode.Callback { - override fun onSetupClick() { + override fun navigateToSetup() { backstack.push(NavTarget.Setup) } - override fun onChangeClick() { + override fun navigateToChange() { backstack.push(NavTarget.Change) } - override fun onDisableClick() { + override fun navigateToDisable() { backstack.push(NavTarget.Disable) } - override fun onConfirmRecoveryKeyClick() { + override fun navigateToEnterRecoveryKey() { backstack.push(NavTarget.EnterRecoveryKey) } } @@ -116,7 +116,7 @@ class SecureBackupFlowNode( if (backstack.canPop()) { backstack.pop() } else { - callbacks.forEach { it.onDone() } + callback.onDone() } } } @@ -125,7 +125,7 @@ class SecureBackupFlowNode( is NavTarget.ResetIdentity -> { val callback = object : ResetIdentityFlowNode.Callback { override fun onDone() { - callbacks.forEach { it.onDone() } + callback.onDone() } } createNode(buildContext, listOf(callback)) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt index 77d1fe8f32..ba4848ca82 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/enter/SecureBackupEnterRecoveryKeyNode.kt @@ -12,10 +12,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -29,7 +29,7 @@ class SecureBackupEnterRecoveryKeyNode( fun onEnterRecoveryKeySuccess() } - private val callback = plugins().first() + private val callback: Callback = callback() @Composable override fun View(modifier: Modifier) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt index dfc9425ebe..5f94d556c7 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/ResetIdentityFlowNode.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.LifecycleOwner 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.push import dev.zacsweers.metro.Assisted @@ -33,6 +32,7 @@ import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTa import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.di.SessionScope @@ -63,6 +63,8 @@ class ResetIdentityFlowNode( fun onDone() } + private val callback: Callback = callback() + sealed interface NavTarget : Parcelable { @Parcelize data object Root : NavTarget @@ -86,7 +88,7 @@ class ResetIdentityFlowNode( cancelResetJob() resetIdentityFlowManager.whenResetIsDone { - plugins().forEach { it.onDone() } + callback.onDone() } } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt index 8267242f97..aee266b249 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/root/ResetIdentityRootNode.kt @@ -15,6 +15,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -27,8 +28,8 @@ class ResetIdentityRootNode( fun onContinue() } + private val callback: Callback = callback() private val presenter = ResetIdentityRootPresenter() - private val callback: Callback = plugins.filterIsInstance().first() @Composable override fun View(modifier: Modifier) { diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt index 6d4db197d3..6b29bb6928 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootNode.kt @@ -14,11 +14,11 @@ import androidx.compose.ui.platform.UriHandler 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.appconfig.LearnMoreConfig +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) @@ -32,27 +32,13 @@ class SecureBackupRootNode( plugins = plugins ) { interface Callback : Plugin { - fun onSetupClick() - fun onChangeClick() - fun onDisableClick() - fun onConfirmRecoveryKeyClick() + fun navigateToSetup() + fun navigateToChange() + fun navigateToDisable() + fun navigateToEnterRecoveryKey() } - private fun onSetupClick() { - plugins().forEach { it.onSetupClick() } - } - - private fun onChangeClick() { - plugins().forEach { it.onChangeClick() } - } - - private fun onDisableClick() { - plugins().forEach { it.onDisableClick() } - } - - private fun onConfirmRecoveryKeyClick() { - plugins().forEach { it.onConfirmRecoveryKeyClick() } - } + private val callback: Callback = callback() private fun onLearnMoreClick(uriHandler: UriHandler) { uriHandler.openUri(LearnMoreConfig.SECURE_BACKUP_URL) @@ -65,10 +51,10 @@ class SecureBackupRootNode( SecureBackupRootView( state = state, onBackClick = ::navigateUp, - onSetupClick = ::onSetupClick, - onChangeClick = ::onChangeClick, - onDisableClick = ::onDisableClick, - onConfirmRecoveryKeyClick = ::onConfirmRecoveryKeyClick, + onSetupClick = callback::navigateToSetup, + onChangeClick = callback::navigateToChange, + onDisableClick = callback::navigateToDisable, + onConfirmRecoveryKeyClick = callback::navigateToEnterRecoveryKey, onLearnMoreClick = { onLearnMoreClick(uriHandler) }, modifier = modifier, ) diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPointTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPointTest.kt index 7741b5141a..04d0030753 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPointTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupEntryPointTest.kt @@ -37,10 +37,12 @@ class DefaultSecureBackupEntryPointTest { override fun onDone() = lambdaError() } val params = SecureBackupEntryPoint.Params(SecureBackupEntryPoint.InitialTarget.ResetIdentity) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(SecureBackupFlowNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareEntryPoint.kt b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareEntryPoint.kt index dd2889e10a..baab6d3d5c 100644 --- a/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareEntryPoint.kt +++ b/features/share/api/src/main/kotlin/io/element/android/features/share/api/ShareEntryPoint.kt @@ -12,21 +12,19 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint -import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.RoomId interface ShareEntryPoint : FeatureEntryPoint { - data class Params(val intent: Intent) : NodeInputs + data class Params(val intent: Intent) - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone(roomIds: List) } - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } } diff --git a/features/share/impl/build.gradle.kts b/features/share/impl/build.gradle.kts index 4c62a06352..dfe4d96543 100644 --- a/features/share/impl/build.gradle.kts +++ b/features/share/impl/build.gradle.kts @@ -46,4 +46,5 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.preferences.test) + testImplementation(projects.libraries.roomselect.test) } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareEntryPoint.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareEntryPoint.kt index 5008c5dbfb..a283bf9d78 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareEntryPoint.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/DefaultShareEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.share.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.features.share.api.ShareEntryPoint import io.element.android.libraries.architecture.createNode @@ -17,23 +16,18 @@ import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultShareEntryPoint : ShareEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ShareEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : ShareEntryPoint.NodeBuilder { - override fun params(params: ShareEntryPoint.Params): ShareEntryPoint.NodeBuilder { - plugins += ShareNode.Inputs(intent = params.intent) - return this - } - - override fun callback(callback: ShareEntryPoint.Callback): ShareEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: ShareEntryPoint.Params, + callback: ShareEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + ShareNode.Inputs(intent = params.intent), + callback, + ) + ) } } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt index 12d2f5bff5..977211ca05 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/ShareNode.kt @@ -23,6 +23,7 @@ import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.share.api.ShareEntryPoint import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -52,7 +53,7 @@ class ShareNode( private val inputs = inputs() private val presenter = presenterFactory.create(inputs.intent) - private val callbacks = plugins.filterIsInstance() + private val callback: ShareEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : RoomSelectEntryPoint.Callback { @@ -61,14 +62,16 @@ class ShareNode( } override fun onCancel() { - onShareDone(emptyList()) + callback.onDone(emptyList()) } } - return roomSelectEntryPoint.nodeBuilder(this, buildContext) - .callback(callback) - .params(RoomSelectEntryPoint.Params(mode = RoomSelectMode.Share)) - .build() + return roomSelectEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = RoomSelectEntryPoint.Params(mode = RoomSelectMode.Share), + callback = callback, + ) } @Composable @@ -82,12 +85,8 @@ class ShareNode( val state = presenter.present() ShareView( state = state, - onShareSuccess = ::onShareDone, + onShareSuccess = callback::onDone, ) } } - - private fun onShareDone(roomIds: List) { - callbacks.forEach { it.onDone(roomIds) } - } } diff --git a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt index d3222edf5e..f5ce67498e 100644 --- a/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt +++ b/features/share/impl/src/main/kotlin/io/element/android/features/share/impl/SharePresenter.kt @@ -63,7 +63,7 @@ class SharePresenter( return ShareState( shareAction = shareActionState.value, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareEntryPointTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareEntryPointTest.kt index 66ee853d26..e3d39cf47a 100644 --- a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareEntryPointTest.kt +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/DefaultShareEntryPointTest.kt @@ -10,12 +10,11 @@ package io.element.android.features.share.impl import android.content.Intent import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.share.api.ShareEntryPoint import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import io.element.android.libraries.roomselect.test.FakeRoomSelectEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import kotlinx.coroutines.test.runTest @@ -37,11 +36,7 @@ class DefaultShareEntryPointTest { buildContext = buildContext, plugins = plugins, presenterFactory = { createSharePresenter() }, - roomSelectEntryPoint = object : RoomSelectEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder { - lambdaError() - } - }, + roomSelectEntryPoint = FakeRoomSelectEntryPoint(), ) } val callback = object : ShareEntryPoint.Callback { @@ -50,10 +45,12 @@ class DefaultShareEntryPointTest { val params = ShareEntryPoint.Params( intent = Intent(), ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(ShareNode::class.java) assertThat(result.plugins).contains(ShareNode.Inputs(params.intent)) assertThat(result.plugins).contains(callback) diff --git a/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt b/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt index 9651327dc5..e0af9f491f 100644 --- a/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt +++ b/features/signedout/api/src/main/kotlin/io/element/android/features/signedout/api/SignedOutEntryPoint.kt @@ -17,10 +17,9 @@ interface SignedOutEntryPoint : FeatureEntryPoint { val sessionId: SessionId, ) - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + ): Node } diff --git a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt index 3bbaebb9b1..7026f0081b 100644 --- a/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt +++ b/features/signedout/impl/src/main/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.signedout.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.signedout.api.SignedOutEntryPoint @@ -17,18 +16,14 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultSignedOutEntryPoint : SignedOutEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SignedOutEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : SignedOutEntryPoint.NodeBuilder { - override fun params(params: SignedOutEntryPoint.Params): SignedOutEntryPoint.NodeBuilder { - plugins += SignedOutNode.Inputs(params.sessionId) - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: SignedOutEntryPoint.Params, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(SignedOutNode.Inputs(params.sessionId)) + ) } } diff --git a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt index 6366ba5ea1..64c48d9a5f 100644 --- a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt +++ b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/DefaultSignedOutEntryPointTest.kt @@ -34,9 +34,11 @@ class DefaultSignedOutEntryPointTest { ) } val params = SignedOutEntryPoint.Params(A_SESSION_ID) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + ) assertThat(result).isInstanceOf(SignedOutNode::class.java) assertThat(result.plugins).contains(SignedOutNode.Inputs(params.sessionId)) } diff --git a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt index e53c0af112..d5a9a910a0 100644 --- a/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt +++ b/features/signedout/impl/src/test/kotlin/io/element/android/features/signedout/impl/SignedOutPresenterTest.kt @@ -58,9 +58,11 @@ class SignedOutPresenterTest { val initialState = awaitItem() assertThat(initialState.signedOutSession).isEqualTo(aSessionData) assertThat(sessionStore.getAllSessions()).isNotEmpty() + assertThat(sessionStore.numberOfSessions()).isEqualTo(1) initialState.eventSink(SignedOutEvents.SignInAgain) assertThat(awaitItem().signedOutSession).isNull() assertThat(sessionStore.getAllSessions()).isEmpty() + assertThat(sessionStore.numberOfSessions()).isEqualTo(0) } } } diff --git a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt index 9dd61c6dff..6b5bd7f892 100644 --- a/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt +++ b/features/space/api/src/main/kotlin/io/element/android/features/space/api/SpaceEntryPoint.kt @@ -15,25 +15,20 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.matrix.api.core.RoomId interface SpaceEntryPoint : FeatureEntryPoint { - fun nodeBuilder( + fun createNode( parentNode: Node, buildContext: BuildContext, - ): NodeBuilder - - interface NodeBuilder { - fun inputs(inputs: Inputs): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + inputs: Inputs, + callback: Callback + ): Node data class Inputs( val roomId: RoomId ) : NodeInputs interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId, viaParameters: List) - fun onOpenDetails() - - fun onOpenMemberList() + fun navigateToRoom(roomId: RoomId, viaParameters: List) + fun navigateToRoomDetails() + fun navigateToRoomMemberList() } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt index 5bcf2cf801..4d30503b80 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.space.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.libraries.architecture.createNode @@ -17,22 +16,15 @@ import io.element.android.libraries.di.SessionScope @ContributesBinding(SessionScope::class) class DefaultSpaceEntryPoint : SpaceEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): SpaceEntryPoint.NodeBuilder { - val plugins = mutableSetOf() - return object : SpaceEntryPoint.NodeBuilder { - override fun inputs(inputs: SpaceEntryPoint.Inputs): SpaceEntryPoint.NodeBuilder { - plugins.add(inputs) - return this - } - - override fun callback(callback: SpaceEntryPoint.Callback): SpaceEntryPoint.NodeBuilder { - plugins.add(callback) - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins = plugins.toList()) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: SpaceEntryPoint.Inputs, + callback: SpaceEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(inputs, callback), + ) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt index f73a3d31ff..1ef496d319 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpaceFlowNode.kt @@ -28,6 +28,7 @@ import io.element.android.features.space.impl.leave.LeaveSpaceNode import io.element.android.features.space.impl.root.SpaceNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner @@ -52,7 +53,7 @@ class SpaceFlowNode( plugins = plugins, ), DependencyInjectionGraphOwner { private val inputs: SpaceEntryPoint.Inputs = inputs() - private val callback = plugins.filterIsInstance().single() + private val callback: SpaceEntryPoint.Callback = callback() private val spaceRoomList = spaceService.spaceRoomList(inputs.roomId) override val graph = graphFactory.create(spaceRoomList) @@ -80,19 +81,19 @@ class SpaceFlowNode( } NavTarget.Root -> { val callback = object : SpaceNode.Callback { - override fun onOpenRoom(roomId: RoomId, viaParameters: List) { - callback.onOpenRoom(roomId, viaParameters) + override fun navigateToRoom(roomId: RoomId, viaParameters: List) { + callback.navigateToRoom(roomId, viaParameters) } - override fun onOpenDetails() { - callback.onOpenDetails() + override fun navigateToRoomDetails() { + callback.navigateToRoomDetails() } - override fun onOpenMemberList() { - callback.onOpenMemberList() + override fun navigateToRoomMemberList() { + callback.navigateToRoomMemberList() } - override fun onLeaveSpace() { + override fun startLeaveSpaceFlow() { backstack.push(NavTarget.Leave) } } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt index f02107dad2..174fa71ee8 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpaceNode.kt @@ -22,6 +22,7 @@ import io.element.android.features.invite.api.acceptdecline.AcceptDeclineInviteV import io.element.android.features.space.impl.di.SpaceFlowScope import io.element.android.libraries.androidutils.R import io.element.android.libraries.androidutils.system.startSharePlainTextIntent +import io.element.android.libraries.architecture.callback import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoomList @@ -40,14 +41,13 @@ class SpaceNode( private val acceptDeclineInviteView: AcceptDeclineInviteView, ) : Node(buildContext, plugins = plugins) { interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId, viaParameters: List) - fun onOpenDetails() - - fun onOpenMemberList() - fun onLeaveSpace() + fun navigateToRoom(roomId: RoomId, viaParameters: List) + fun navigateToRoomDetails() + fun navigateToRoomMemberList() + fun startLeaveSpaceFlow() } - private val callback = plugins.filterIsInstance().single() + private val callback: Callback = callback() private fun onShareRoom(context: Context) = lifecycleScope.launch { matrixClient.getRoom(spaceRoomList.roomId)?.use { room -> @@ -74,25 +74,25 @@ class SpaceNode( state = state, onBackClick = ::navigateUp, onLeaveSpaceClick = { - callback.onLeaveSpace() + callback.startLeaveSpaceFlow() }, onRoomClick = { spaceRoom -> - callback.onOpenRoom(spaceRoom.roomId, spaceRoom.via) + callback.navigateToRoom(spaceRoom.roomId, spaceRoom.via) }, onDetailsClick = { - callback.onOpenDetails() + callback.navigateToRoomDetails() }, onShareSpace = { onShareRoom(context) }, onViewMembersClick = { - callback.onOpenMemberList() + callback.navigateToRoomMemberList() }, acceptDeclineInviteView = { acceptDeclineInviteView.Render( state = state.acceptDeclineInviteState, onAcceptInviteSuccess = { roomId -> - callback.onOpenRoom(roomId, emptyList()) + callback.navigateToRoom(roomId, emptyList()) }, onDeclineInviteSuccess = { roomId -> // No action needed diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt index 2d43ef0d39..3fd260dd4f 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt @@ -44,14 +44,16 @@ class DefaultSpaceEntryPointTest { ) } val callback = object : SpaceEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId, viaParameters: List) = lambdaError() - override fun onOpenDetails() = lambdaError() - override fun onOpenMemberList() = lambdaError() + override fun navigateToRoom(roomId: RoomId, viaParameters: List) = lambdaError() + override fun navigateToRoomDetails() = lambdaError() + override fun navigateToRoomMemberList() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .inputs(nodeInputs) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + inputs = nodeInputs, + callback = callback, + ) assertThat(result).isInstanceOf(SpaceFlowNode::class.java) assertThat(result.plugins).contains(nodeInputs) assertThat(result.plugins).contains(callback) diff --git a/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartChatEntryPoint.kt b/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartChatEntryPoint.kt index 17b9b902e2..0e16e05967 100644 --- a/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartChatEntryPoint.kt +++ b/features/startchat/api/src/main/kotlin/io/element/android/features/startchat/api/StartChatEntryPoint.kt @@ -14,14 +14,14 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.RoomIdOrAlias interface StartChatEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { - fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) - fun onOpenRoomDirectory() + fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) + fun navigateToRoomDirectory() } } diff --git a/features/startchat/impl/build.gradle.kts b/features/startchat/impl/build.gradle.kts index 8ba7593b36..0f1c513db0 100644 --- a/features/startchat/impl/build.gradle.kts +++ b/features/startchat/impl/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.usersearch.test) + testImplementation(projects.features.createroom.test) testImplementation(projects.features.startchat.test) testImplementation(projects.libraries.featureflag.test) } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt index a45b4dddab..70babb2928 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt @@ -17,7 +17,7 @@ import io.element.android.libraries.architecture.overlay.operation.show import io.element.android.libraries.matrix.api.core.RoomIdOrAlias interface StartChatNavigator : Plugin { - fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) + fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) fun onCreateNewRoom() fun onShowJoinRoomByAddress() fun onDismissJoinRoomByAddress() @@ -30,7 +30,8 @@ class DefaultStartChatNavigator( private val openRoom: (RoomIdOrAlias, List) -> Unit, private val openRoomDirectory: () -> Unit, ) : StartChatNavigator { - override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) = openRoom(roomIdOrAlias, serverNames) + override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) = + openRoom(roomIdOrAlias, serverNames) override fun onOpenRoomDirectory() = openRoomDirectory() diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPoint.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPoint.kt index 801d5e0466..fa28c882c7 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPoint.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.startchat.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.startchat.api.StartChatEntryPoint @@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultStartChatEntryPoint : StartChatEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): StartChatEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : StartChatEntryPoint.NodeBuilder { - override fun callback(callback: StartChatEntryPoint.Callback): StartChatEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: StartChatEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt index 30d1f3a2cf..ee30f4997c 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt @@ -16,7 +16,6 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.navigation.transition.JumpToEndTransitionHandler import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin -import com.bumble.appyx.core.plugin.plugins import com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject @@ -29,6 +28,7 @@ import io.element.android.features.startchat.impl.root.StartChatNode import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.OverlayView +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.RoomId @@ -60,15 +60,12 @@ class StartChatFlowNode( data object JoinByAddress : NavTarget } + private val callback: StartChatEntryPoint.Callback = callback() private val navigator = DefaultStartChatNavigator( backstack = backstack, overlay = overlay, - openRoom = { roomIdOrAlias, viaServers -> - plugins().forEach { it.onOpenRoom(roomIdOrAlias, viaServers) } - }, - openRoomDirectory = { - plugins().forEach { it.onOpenRoomDirectory() } - } + openRoom = callback::onRoomCreated, + openRoomDirectory = callback::navigateToRoomDirectory, ) override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -79,12 +76,14 @@ class StartChatFlowNode( NavTarget.NewRoom -> { val callback = object : CreateRoomEntryPoint.Callback { override fun onRoomCreated(roomId: RoomId) { - navigator.onOpenRoom(roomId.toRoomIdOrAlias(), emptyList()) + navigator.onRoomCreated(roomId.toRoomIdOrAlias(), emptyList()) } } - createRoomEntryPoint.nodeBuilder(parentNode = this, buildContext = buildContext) - .callback(callback) - .build() + createRoomEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + callback = callback, + ) } NavTarget.JoinByAddress -> { createNode(buildContext = buildContext, plugins = listOf(navigator)) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt index 540c1a4784..350a59de73 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt @@ -94,7 +94,7 @@ class JoinRoomByAddressPresenter( private fun onRoomFound(state: RoomAddressState.RoomFound) { navigator.onDismissJoinRoomByAddress() - navigator.onOpenRoom( + navigator.onRoomCreated( roomIdOrAlias = state.resolved.roomId.toRoomIdOrAlias(), serverNames = state.resolved.servers ) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt index 9a9ca85160..b5cc7f2506 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt @@ -53,7 +53,7 @@ class StartChatNode( onCloseClick = this::navigateUp, onNewRoomClick = navigator::onCreateNewRoom, onOpenDM = { - navigator.onOpenRoom(roomIdOrAlias = it.toRoomIdOrAlias(), serverNames = emptyList()) + navigator.onRoomCreated(roomIdOrAlias = it.toRoomIdOrAlias(), serverNames = emptyList()) }, onJoinByAddressClick = navigator::onShowJoinRoomByAddress, onInviteFriendsClick = { invitePeople(activity) }, diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPointTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPointTest.kt index 8f4a41a3fa..1792567a87 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPointTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/DefaultStartChatEntryPointTest.kt @@ -9,10 +9,9 @@ package io.element.android.features.startchat.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.createroom.api.CreateRoomEntryPoint +import io.element.android.features.createroom.api.FakeCreateRoomEntryPoint import io.element.android.features.startchat.api.StartChatEntryPoint import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.tests.testutils.lambda.lambdaError @@ -34,18 +33,18 @@ class DefaultStartChatEntryPointTest { StartChatFlowNode( buildContext = buildContext, plugins = plugins, - createRoomEntryPoint = object : CreateRoomEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + createRoomEntryPoint = FakeCreateRoomEntryPoint(), ) } val callback = object : StartChatEntryPoint.Callback { - override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) = lambdaError() - override fun onOpenRoomDirectory() = lambdaError() + override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) = lambdaError() + override fun navigateToRoomDirectory() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(StartChatFlowNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt index 9de00e0a4c..95a8801fd7 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt @@ -17,7 +17,7 @@ class FakeStartChatNavigator( private val dismissJoinRoomByAddressLambda: () -> Unit = {}, private val openRoomDirectoryLambda: () -> Unit = {}, ) : StartChatNavigator { - override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List) { + override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List) { openRoomLambda(roomIdOrAlias, serverNames) } diff --git a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEntryPoint.kt b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEntryPoint.kt index 70968aad61..ef3b0afeaa 100644 --- a/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEntryPoint.kt +++ b/features/userprofile/api/src/main/kotlin/io/element/android/features/userprofile/api/UserProfileEntryPoint.kt @@ -19,14 +19,13 @@ interface UserProfileEntryPoint : FeatureEntryPoint { data class Params(val userId: UserId) : NodeInputs interface Callback : Plugin { - fun onOpenRoom(roomId: RoomId) + fun navigateToRoom(roomId: RoomId) } - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } - - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node } diff --git a/features/userprofile/impl/build.gradle.kts b/features/userprofile/impl/build.gradle.kts index f0c214c22e..b8a5506dd4 100644 --- a/features/userprofile/impl/build.gradle.kts +++ b/features/userprofile/impl/build.gradle.kts @@ -44,6 +44,9 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.mediaviewer.test) + testImplementation(projects.features.call.test) + testImplementation(projects.features.verifysession.test) testImplementation(projects.features.startchat.test) testImplementation(projects.features.enterprise.test) } diff --git a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPoint.kt b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPoint.kt index 84f1b728e6..e69ef185c0 100644 --- a/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPoint.kt +++ b/features/userprofile/impl/src/main/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.userprofile.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.userprofile.api.UserProfileEntryPoint @@ -17,23 +16,15 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultUserProfileEntryPoint : UserProfileEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): UserProfileEntryPoint.NodeBuilder { - return object : UserProfileEntryPoint.NodeBuilder { - val plugins = ArrayList() - - override fun params(params: UserProfileEntryPoint.Params): UserProfileEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun callback(callback: UserProfileEntryPoint.Callback): UserProfileEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: UserProfileEntryPoint.Params, + callback: UserProfileEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback), + ) } } 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 da3adef973..0b36452b45 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 @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -28,6 +27,7 @@ import io.element.android.features.userprofile.shared.UserProfileNodeHelper import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -67,25 +67,26 @@ class UserProfileFlowNode( data class VerifyUser(val userId: UserId) : NavTarget } + private val callback: UserProfileEntryPoint.Callback = callback() private val inputs = inputs() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : UserProfileNodeHelper.Callback { - override fun openAvatarPreview(username: String, avatarUrl: String) { + override fun navigateToAvatarPreview(username: String, avatarUrl: String) { backstack.push(NavTarget.AvatarPreview(username, avatarUrl)) } - override fun onStartDM(roomId: RoomId) { - plugins().forEach { it.onOpenRoom(roomId) } + override fun navigateToRoom(roomId: RoomId) { + callback.navigateToRoom(roomId) } - override fun onStartCall(dmRoomId: RoomId) { + override fun startCall(dmRoomId: RoomId) { elementCallEntryPoint.startCall(CallType.RoomCall(sessionId = sessionId, roomId = dmRoomId)) } - override fun onVerifyUser(userId: UserId) { + override fun startVerifyUserFlow(userId: UserId) { backstack.push(NavTarget.VerifyUser(userId)) } } @@ -98,30 +99,48 @@ class UserProfileFlowNode( backstack.pop() } - override fun onViewInTimeline(eventId: EventId) { + override fun viewInTimeline(eventId: EventId) { // Cannot happen } - override fun onForwardEvent(eventId: EventId) { + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { // Cannot happen } } - mediaViewerEntryPoint.nodeBuilder(this, buildContext) - .avatar( - filename = navTarget.name, - avatarUrl = navTarget.avatarUrl - ) - .callback(callback) - .build() + val params = mediaViewerEntryPoint.createParamsForAvatar( + filename = navTarget.name, + avatarUrl = navTarget.avatarUrl, + ) + mediaViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = callback, + ) } is NavTarget.VerifyUser -> { val params = OutgoingVerificationEntryPoint.Params( showDeviceVerifiedScreen = false, verificationRequest = VerificationRequest.Outgoing.User(userId = navTarget.userId) ) - outgoingVerificationEntryPoint.nodeBuilder(this, buildContext) - .params(params) - .build() + outgoingVerificationEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = params, + callback = object : OutgoingVerificationEntryPoint.Callback { + override fun navigateToLearnMoreAboutEncryption() { + // No op + } + + override fun onBack() { + // No op + } + + override fun onDone() { + // No op + } + } + ) } } } 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 735957946a..c182bcb26c 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 @@ -63,7 +63,7 @@ class UserProfileNode( } fun onStartDM(roomId: RoomId) { - callback.onStartDM(roomId) + callback.navigateToRoom(roomId) } val state = presenter.present() @@ -74,9 +74,9 @@ class UserProfileNode( goBack = this::navigateUp, onShareUser = ::onShareUser, onOpenDm = ::onStartDM, - onStartCall = callback::onStartCall, - openAvatarPreview = callback::openAvatarPreview, - onVerifyClick = callback::onVerifyUser, + onStartCall = callback::startCall, + openAvatarPreview = callback::navigateToAvatarPreview, + onVerifyClick = callback::startVerifyUserFlow, ) } } diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt index 75bc434048..eca2b4b382 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/DefaultUserProfileEntryPointTest.kt @@ -9,19 +9,15 @@ package io.element.android.features.userprofile.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.api.CallType -import io.element.android.features.call.api.ElementCallEntryPoint +import io.element.android.features.call.test.FakeElementCallEntryPoint import io.element.android.features.userprofile.api.UserProfileEntryPoint -import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint -import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.features.verifysession.test.FakeOutgoingVerificationEntryPoint 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.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID -import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.libraries.mediaviewer.test.FakeMediaViewerEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import org.junit.Rule @@ -43,41 +39,25 @@ class DefaultUserProfileEntryPointTest { buildContext = buildContext, plugins = plugins, sessionId = A_SESSION_ID, - elementCallEntryPoint = object : ElementCallEntryPoint { - override fun startCall(callType: CallType) = lambdaError() - override suspend fun handleIncomingCall( - callType: CallType.RoomCall, - eventId: EventId, - senderId: UserId, - roomName: String?, - senderName: String?, - avatarUrl: String?, - timestamp: Long, - expirationTimestamp: Long, - notificationChannelId: String, - textContent: String? - ) = lambdaError() - }, - mediaViewerEntryPoint = object : MediaViewerEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, - outgoingVerificationEntryPoint = object : OutgoingVerificationEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + elementCallEntryPoint = FakeElementCallEntryPoint(), + mediaViewerEntryPoint = FakeMediaViewerEntryPoint(), + outgoingVerificationEntryPoint = FakeOutgoingVerificationEntryPoint(), ) } val callback = object : UserProfileEntryPoint.Callback { - override fun onOpenRoom(roomId: RoomId) { + override fun navigateToRoom(roomId: RoomId) { lambdaError() } } val params = UserProfileEntryPoint.Params( userId = A_USER_ID, ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(UserProfileFlowNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) 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 61f4669769..af5f9691e0 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 @@ -21,10 +21,10 @@ class UserProfileNodeHelper( private val userId: UserId, ) { interface Callback : NodeInputs { - fun openAvatarPreview(username: String, avatarUrl: String) - fun onStartDM(roomId: RoomId) - fun onStartCall(dmRoomId: RoomId) - fun onVerifyUser(userId: UserId) + fun navigateToAvatarPreview(username: String, avatarUrl: String) + fun navigateToRoom(roomId: RoomId) + fun startCall(dmRoomId: RoomId) + fun startVerifyUserFlow(userId: UserId) } fun onShareUser( 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 9d90f33e8d..e6ea0ed687 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 @@ -19,13 +19,12 @@ interface IncomingVerificationEntryPoint : FeatureEntryPoint { val verificationRequest: VerificationRequest.Incoming, ) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun params(params: Params): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/OutgoingVerificationEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/OutgoingVerificationEntryPoint.kt index 60536504d2..f6c1f41a0b 100644 --- a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/OutgoingVerificationEntryPoint.kt +++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/OutgoingVerificationEntryPoint.kt @@ -20,16 +20,15 @@ interface OutgoingVerificationEntryPoint : FeatureEntryPoint { val verificationRequest: VerificationRequest.Outgoing, ) : NodeInputs - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun params(params: Params): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { - fun onLearnMoreAboutEncryption() + fun navigateToLearnMoreAboutEncryption() fun onBack() fun onDone() } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt index b1d890b9ef..9d6d91c79a 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.verifysession.impl.incoming import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint @@ -17,23 +16,12 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultIncomingVerificationEntryPoint : IncomingVerificationEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): IncomingVerificationEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : IncomingVerificationEntryPoint.NodeBuilder { - override fun callback(callback: IncomingVerificationEntryPoint.Callback): IncomingVerificationEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun params(params: IncomingVerificationEntryPoint.Params): IncomingVerificationEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: IncomingVerificationEntryPoint.Params, + callback: IncomingVerificationEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(params, callback)) } } 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 a17054b9e6..fed3014925 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 @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -28,13 +28,14 @@ class IncomingVerificationNode( presenterFactory: IncomingVerificationPresenter.Factory, ) : Node(buildContext, plugins = plugins), IncomingVerificationNavigator { + private val callback: IncomingVerificationEntryPoint.Callback = callback() private val presenter = presenterFactory.create( verificationRequest = inputs().verificationRequest, navigator = this, ) override fun onFinish() { - plugins().forEach { it.onDone() } + callback.onDone() } @Composable diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPoint.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPoint.kt index a6b587308a..3328b1df1e 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPoint.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.verifysession.impl.outgoing import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint @@ -17,23 +16,12 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultOutgoingVerificationEntryPoint : OutgoingVerificationEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): OutgoingVerificationEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : OutgoingVerificationEntryPoint.NodeBuilder { - override fun callback(callback: OutgoingVerificationEntryPoint.Callback): OutgoingVerificationEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun params(params: OutgoingVerificationEntryPoint.Params): OutgoingVerificationEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: OutgoingVerificationEntryPoint.Params, + callback: OutgoingVerificationEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(params, callback)) } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt index 9941ce58fe..fa5ba8d3f8 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/outgoing/OutgoingVerificationNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope @@ -27,8 +27,7 @@ class OutgoingVerificationNode( @Assisted plugins: List, presenterFactory: OutgoingVerificationPresenter.Factory, ) : Node(buildContext, plugins = plugins) { - private val callback = plugins().first() - + private val callback: OutgoingVerificationEntryPoint.Callback = callback() private val inputs = inputs() private val presenter = presenterFactory.create( @@ -42,7 +41,7 @@ class OutgoingVerificationNode( OutgoingVerificationView( state = state, modifier = modifier, - onLearnMoreClick = callback::onLearnMoreAboutEncryption, + onLearnMoreClick = callback::navigateToLearnMoreAboutEncryption, onFinish = callback::onDone, onBack = callback::onBack, ) diff --git a/features/verifysession/impl/src/main/res/values-fa/translations.xml b/features/verifysession/impl/src/main/res/values-fa/translations.xml index fc0bace5cd..ffc03e1f19 100644 --- a/features/verifysession/impl/src/main/res/values-fa/translations.xml +++ b/features/verifysession/impl/src/main/res/values-fa/translations.xml @@ -11,10 +11,10 @@ "استفاده از افزاره‌ای دیگر" "منتظر افزارهٔ دیگر…" "يه چيزي درست به نظر نمياد یا زمان درخواست به پایان رسید یا درخواست رد شد." - "تأیید کنید که ایموجی های زیر با ایموجی های نشان داده شده در جلسه دیگر شما مطابقت دارند." + "تأیید تطابق شکلک‌های زیر با شکلک‌های نشان داده شده روی افزارهٔ دیگرتان." "مقایسهٔ شکلک‌ها" "مقایسهٔ اعداد" - "اکنون نشست جدیدتان تأیید شده‌. این نشست به پیام‌های رمزنگارش شده‌تان دسترسی داشته و دیگر کاربران مطمئن می‌بینندش." + "اکنون می‌توانید روی افزارهٔ دیگرتان با امنیت پیام فرستاده و بخوانید." "افزاره تأیید شده" "ورود کلید بازیابی" "برای دسترسی به تاریخچه پیام‌های رمزگذاری‌شده‌تان، ثابت کنید که خودتان هستید." @@ -26,7 +26,7 @@ "شکلک‌ها را مقایسه کنید، از ترتیب نمایش آنان نیز مطمئن شوید." "وارد شده" "صحت‌سنجی شکست خورد" - "اکنون نشست جدیدتان تأیید شده‌. این نشست به پیام‌های رمزنگارش شده‌تان دسترسی داشته و دیگر کاربران مطمئن می‌بینندش." + "اکنون می‌توانید روی افزارهٔ دیگرتان با امنیت پیام فرستاده و بخوانید." "افزاره تأیید شده" "مطابق نیستند" "مطابقند" diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPointTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPointTest.kt index ad586fc7fb..437657b811 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPointTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/incoming/DefaultIncomingVerificationEntryPointTest.kt @@ -37,10 +37,12 @@ class DefaultIncomingVerificationEntryPointTest { val params = IncomingVerificationEntryPoint.Params( verificationRequest = anIncomingSessionVerificationRequest() ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(IncomingVerificationNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPointTest.kt b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPointTest.kt index 52ff36dbd6..80906bc86a 100644 --- a/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPointTest.kt +++ b/features/verifysession/impl/src/test/kotlin/io/element/android/features/verifysession/impl/outgoing/DefaultOutgoingVerificationEntryPointTest.kt @@ -34,7 +34,7 @@ class DefaultOutgoingVerificationEntryPointTest { ) } val callback = object : OutgoingVerificationEntryPoint.Callback { - override fun onLearnMoreAboutEncryption() = lambdaError() + override fun navigateToLearnMoreAboutEncryption() = lambdaError() override fun onBack() = lambdaError() override fun onDone() = lambdaError() } @@ -42,10 +42,12 @@ class DefaultOutgoingVerificationEntryPointTest { showDeviceVerifiedScreen = true, verificationRequest = anOutgoingSessionVerificationRequest(), ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(OutgoingVerificationNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) diff --git a/features/verifysession/test/build.gradle.kts b/features/verifysession/test/build.gradle.kts new file mode 100644 index 0000000000..f392f8cc6d --- /dev/null +++ b/features/verifysession/test/build.gradle.kts @@ -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. + */ + +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.verifysession.test" +} + +dependencies { + implementation(projects.features.verifysession.api) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) +} diff --git a/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeIncomingVerificationEntryPoint.kt b/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeIncomingVerificationEntryPoint.kt new file mode 100644 index 0000000000..1001c636e4 --- /dev/null +++ b/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeIncomingVerificationEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeIncomingVerificationEntryPoint : IncomingVerificationEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: IncomingVerificationEntryPoint.Params, + callback: IncomingVerificationEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeOutgoingVerificationEntryPoint.kt b/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeOutgoingVerificationEntryPoint.kt new file mode 100644 index 0000000000..8deca450cd --- /dev/null +++ b/features/verifysession/test/src/main/kotlin/io/element/android/features/verifysession/test/FakeOutgoingVerificationEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.verifysession.api.OutgoingVerificationEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeOutgoingVerificationEntryPoint : OutgoingVerificationEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: OutgoingVerificationEntryPoint.Params, + callback: OutgoingVerificationEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/ViewFolderEntryPoint.kt b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/ViewFolderEntryPoint.kt index e7f6f580a7..9d756a4529 100644 --- a/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/ViewFolderEntryPoint.kt +++ b/features/viewfolder/api/src/main/kotlin/io/element/android/features/viewfolder/api/ViewFolderEntryPoint.kt @@ -17,13 +17,12 @@ interface ViewFolderEntryPoint : FeatureEntryPoint { val rootPath: String, ) - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt index 2aa341d5b4..4261c17e72 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.features.viewfolder.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.features.viewfolder.api.ViewFolderEntryPoint @@ -18,23 +17,18 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultViewFolderEntryPoint : ViewFolderEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ViewFolderEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : ViewFolderEntryPoint.NodeBuilder { - override fun params(params: ViewFolderEntryPoint.Params): ViewFolderEntryPoint.NodeBuilder { - plugins += ViewFolderFlowNode.Inputs(params.rootPath) - return this - } - - override fun callback(callback: ViewFolderEntryPoint.Callback): ViewFolderEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: ViewFolderEntryPoint.Params, + callback: ViewFolderEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + ViewFolderFlowNode.Inputs(params.rootPath), + callback, + ), + ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt index 41369dda07..e474073e6f 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileNode.kt @@ -12,12 +12,12 @@ 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) @@ -36,6 +36,7 @@ class ViewFileNode( fun onBackClick() } + private val callback: Callback = callback() private val inputs: Inputs = inputs() private val presenter = presenterFactory.create( @@ -43,17 +44,13 @@ class ViewFileNode( name = inputs.name, ) - private fun onBackClick() { - plugins().forEach { it.onBackClick() } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() ViewFileView( state = state, modifier = modifier, - onBackClick = ::onBackClick, + onBackClick = callback::onBackClick, ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt index 4c57ea4135..faea81c084 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderNode.kt @@ -12,13 +12,13 @@ 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 dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs @ContributesNode(AppScope::class) @@ -35,9 +35,10 @@ class ViewFolderNode( interface Callback : Plugin { fun onBackClick() - fun onNavigateTo(item: Item) + fun navigateToItem(item: Item) } + private val callback: Callback = callback() private val inputs: Inputs = inputs() private val presenter = presenterFactory.create( @@ -45,22 +46,14 @@ class ViewFolderNode( path = inputs.path, ) - private fun onBackClick() { - plugins().forEach { it.onBackClick() } - } - - private fun onNavigateTo(item: Item) { - plugins().forEach { it.onNavigateTo(item) } - } - @Composable override fun View(modifier: Modifier) { val state = presenter.present() ViewFolderView( state = state, modifier = modifier, - onNavigateTo = ::onNavigateTo, - onBackClick = ::onBackClick, + onNavigateTo = callback::navigateToItem, + onBackClick = callback::onBackClick, ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt index d57824f2fd..4695576f9d 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/root/ViewFolderFlowNode.kt @@ -13,7 +13,6 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.push @@ -28,6 +27,7 @@ import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import kotlinx.parcelize.Parcelize @@ -65,6 +65,7 @@ class ViewFolderFlowNode( val rootPath: String, ) : NodeInputs + private val callback: ViewFolderEntryPoint.Callback = callback() private val inputs: Inputs = inputs() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -108,10 +109,10 @@ class ViewFolderFlowNode( ): Node { val callback: ViewFolderNode.Callback = object : ViewFolderNode.Callback { override fun onBackClick() { - onDone() + callback.onDone() } - override fun onNavigateTo(item: Item) { + override fun navigateToItem(item: Item) { when (item) { Item.Parent -> { // Should not happen when in Root since parent is not accessible from root (canGoUp set to false) @@ -133,8 +134,4 @@ class ViewFolderFlowNode( override fun View(modifier: Modifier) { BackstackView() } - - private fun onDone() { - plugins().forEach { it.onDone() } - } } diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPointTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPointTest.kt index 47d5992d8c..a8e643e1f5 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPointTest.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/DefaultViewFolderEntryPointTest.kt @@ -40,10 +40,12 @@ class DefaultViewFolderEntryPointTest { val params = ViewFolderEntryPoint.Params( rootPath = "path", ) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(ViewFolderFlowNode::class.java) assertThat(result.plugins).contains(ViewFolderFlowNode.Inputs(params.rootPath)) assertThat(result.plugins).contains(callback) diff --git a/features/viewfolder/test/build.gradle.kts b/features/viewfolder/test/build.gradle.kts new file mode 100644 index 0000000000..33bdfe03e2 --- /dev/null +++ b/features/viewfolder/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.features.viewfolder.test" +} + +dependencies { + implementation(projects.features.viewfolder.api) + implementation(projects.libraries.architecture) + implementation(projects.tests.testutils) +} diff --git a/features/viewfolder/test/src/main/kotlin/io/element/android/features/viewfolder/test/FakeViewFolderEntryPoint.kt b/features/viewfolder/test/src/main/kotlin/io/element/android/features/viewfolder/test/FakeViewFolderEntryPoint.kt new file mode 100644 index 0000000000..da908bd925 --- /dev/null +++ b/features/viewfolder/test/src/main/kotlin/io/element/android/features/viewfolder/test/FakeViewFolderEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.features.viewfolder.api.ViewFolderEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeViewFolderEntryPoint : ViewFolderEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: ViewFolderEntryPoint.Params, + callback: ViewFolderEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 64085f7306..d17795bcca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ android_gradle_plugin = "8.11.1" kotlin = "2.2.20" kotlinpoet = "2.2.0" ksp = "2.2.20-2.0.2" -firebaseAppDistribution = "5.1.1" +firebaseAppDistribution = "5.2.0" # AndroidX core = "1.17.0" @@ -33,7 +33,7 @@ accompanist = "0.37.3" # Test test_core = "1.7.0" -roborazzi = "1.50.0" +roborazzi = "1.51.0" # Jetbrain datetime = "0.7.1" @@ -49,10 +49,10 @@ telephoto = "0.18.0" haze = "1.6.10" # Dependency analysis -dependencyAnalysis = "3.4.0" +dependencyAnalysis = "3.4.1" # DI -metro = "0.7.2" +metro = "0.7.3" # Auto service autoservice = "1.1.1" @@ -78,7 +78,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:34.4.0" +google_firebase_bom = "com.google.firebase:firebase-bom:34.5.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" } @@ -141,7 +141,7 @@ accompanist_permission = { module = "com.google.accompanist:accompanist-permissi squareup_seismic = "com.squareup:seismic:1.0.3" # network -network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:5.2.1" +network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:5.3.0" network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" } network_okhttp_okhttp = { module = "com.squareup.okhttp3:okhttp" } network_okhttp = { module = "com.squareup.okhttp3:okhttp" } @@ -177,7 +177,7 @@ test_detekt_test = { module = "io.gitlab.arturbosch.detekt:detekt-test", version # https://github.com/matrix-org/matrix-rust-components-kotlin/commits/main/sdk/sdk-android/src/main/kotlin/org/matrix/rustcomponents/sdk/matrix_sdk_ffi.kt # All new features should not be implemented in the pull request that upgrades the version, developers should # only fix API breaks and may add some TODOs. -matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.10.29" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.10.31" # Others coil = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } @@ -229,7 +229,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0" metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } # Element Call -element_call_embedded = "io.element.android:element-call-embedded:0.16.0" +element_call_embedded = "io.element.android:element-call-embedded:0.16.1" # Auto services google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } diff --git a/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt b/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt index 72da3491de..c68ec5d95b 100644 --- a/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt +++ b/libraries/accountselect/api/src/main/kotlin/io/element/android/libraries/accountselect/api/AccountSelectEntryPoint.kt @@ -14,15 +14,14 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.SessionId interface AccountSelectEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { - fun onSelectAccount(sessionId: SessionId) + fun onAccountSelected(sessionId: SessionId) fun onCancel() } } diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt index 5478d9fe43..c1a0fbff65 100644 --- a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/AccountSelectNode.kt @@ -17,7 +17,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.architecture.callback @ContributesNode(AppScope::class) @AssistedInject @@ -26,23 +26,15 @@ class AccountSelectNode( @Assisted plugins: List, private val presenter: AccountSelectPresenter, ) : Node(buildContext, plugins = plugins) { - private val callbacks = plugins.filterIsInstance() - - private fun onDismiss() { - callbacks.forEach { it.onCancel() } - } - - private fun onSelectAccount(sessionId: SessionId) { - callbacks.forEach { it.onSelectAccount(sessionId) } - } + private val callback: AccountSelectEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() AccountSelectView( state = state, - onDismiss = ::onDismiss, - onSelectAccount = ::onSelectAccount, + onDismiss = callback::onCancel, + onSelectAccount = callback::onAccountSelected, modifier = modifier, ) } diff --git a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt index bd35d3e6f0..5b6381ce13 100644 --- a/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt +++ b/libraries/accountselect/impl/src/main/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.accountselect.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint @@ -17,18 +16,11 @@ import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) class DefaultAccountSelectEntryPoint : AccountSelectEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): AccountSelectEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : AccountSelectEntryPoint.NodeBuilder { - override fun callback(callback: AccountSelectEntryPoint.Callback): AccountSelectEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: AccountSelectEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt index d61dcc89ba..b36fcb5ab8 100644 --- a/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt +++ b/libraries/accountselect/impl/src/test/kotlin/io/element/android/libraries/accountselect/impl/DefaultAccountSelectEntryPointTest.kt @@ -32,12 +32,14 @@ class DefaultAccountSelectEntryPointTest { ) } val callback = object : AccountSelectEntryPoint.Callback { - override fun onSelectAccount(sessionId: SessionId) = lambdaError() + override fun onAccountSelected(sessionId: SessionId) = lambdaError() override fun onCancel() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(AccountSelectNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt new file mode 100644 index 0000000000..5054f62290 --- /dev/null +++ b/libraries/architecture/src/main/kotlin/io/element/android/libraries/architecture/NodeCallback.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2023, 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.architecture + +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import com.bumble.appyx.core.plugin.plugins + +inline fun Node.callback(): I { + return requireNotNull(plugins().singleOrNull()) { "Make sure to actually pass a Callback plugin to your node" } +} diff --git a/libraries/compound/build.gradle.kts b/libraries/compound/build.gradle.kts index cbdb09d451..933ef3e495 100644 --- a/libraries/compound/build.gradle.kts +++ b/libraries/compound/build.gradle.kts @@ -18,12 +18,12 @@ android { testOptions { unitTests.isIncludeAndroidResources = true } - - dependencies { - implementation(libs.showkase) - testCommonDependencies(libs) - testImplementation(libs.test.roborazzi) - testImplementation(libs.test.roborazzi.compose) - testImplementation(libs.test.roborazzi.junit) - } +} + +dependencies { + implementation(libs.showkase) + testCommonDependencies(libs) + testImplementation(libs.test.roborazzi) + testImplementation(libs.test.roborazzi.compose) + testImplementation(libs.test.roborazzi.junit) } diff --git a/libraries/cryptography/test/build.gradle.kts b/libraries/cryptography/test/build.gradle.kts index 5564c972f9..e3a110d8fb 100644 --- a/libraries/cryptography/test/build.gradle.kts +++ b/libraries/cryptography/test/build.gradle.kts @@ -11,8 +11,8 @@ plugins { android { namespace = "io.element.android.libraries.cryptography.test" - - dependencies { - api(projects.libraries.cryptography.api) - } +} + +dependencies { + api(projects.libraries.cryptography.api) } diff --git a/libraries/dateformatter/api/build.gradle.kts b/libraries/dateformatter/api/build.gradle.kts index cebb9d4049..99c22515fd 100644 --- a/libraries/dateformatter/api/build.gradle.kts +++ b/libraries/dateformatter/api/build.gradle.kts @@ -13,8 +13,8 @@ plugins { android { namespace = "io.element.android.libraries.dateformatter.api" - - dependencies { - testCommonDependencies(libs) - } +} + +dependencies { + testCommonDependencies(libs) } diff --git a/libraries/dateformatter/impl/build.gradle.kts b/libraries/dateformatter/impl/build.gradle.kts index 72da2f81f6..15c0034f91 100644 --- a/libraries/dateformatter/impl/build.gradle.kts +++ b/libraries/dateformatter/impl/build.gradle.kts @@ -30,19 +30,19 @@ android { ) } } - - dependencies { - implementation(projects.libraries.core) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.di) - implementation(projects.libraries.uiStrings) - implementation(projects.services.toolbox.api) - - api(projects.libraries.dateformatter.api) - api(libs.datetime) - - testCommonDependencies(libs, true) - testImplementation(projects.libraries.dateformatter.test) - testImplementation(projects.services.toolbox.test) - } +} + +dependencies { + implementation(projects.libraries.core) + implementation(projects.libraries.designsystem) + implementation(projects.libraries.di) + implementation(projects.libraries.uiStrings) + implementation(projects.services.toolbox.api) + + api(projects.libraries.dateformatter.api) + api(libs.datetime) + + testCommonDependencies(libs, true) + testImplementation(projects.libraries.dateformatter.test) + testImplementation(projects.services.toolbox.test) } diff --git a/libraries/dateformatter/test/build.gradle.kts b/libraries/dateformatter/test/build.gradle.kts index 6f3877ea80..af8b2a8e19 100644 --- a/libraries/dateformatter/test/build.gradle.kts +++ b/libraries/dateformatter/test/build.gradle.kts @@ -11,9 +11,9 @@ plugins { android { namespace = "io.element.android.libraries.dateformatter.test" - - dependencies { - api(projects.libraries.dateformatter.api) - api(libs.datetime) - } +} + +dependencies { + api(projects.libraries.dateformatter.api) + api(libs.datetime) } diff --git a/libraries/designsystem/build.gradle.kts b/libraries/designsystem/build.gradle.kts index ff0b4878b2..8750b04683 100644 --- a/libraries/designsystem/build.gradle.kts +++ b/libraries/designsystem/build.gradle.kts @@ -25,24 +25,24 @@ android { consumerProguardFiles("consumer-rules.pro") } } - - dependencies { - api(projects.libraries.compound) - - implementation(libs.androidx.compose.material3.windowsizeclass) - implementation(libs.androidx.compose.material3.adaptive) - implementation(libs.coil.compose) - implementation(libs.vanniktech.blurhash) - implementation(projects.libraries.androidutils) - implementation(projects.libraries.architecture) - implementation(projects.libraries.core) - implementation(projects.libraries.preferences.api) - implementation(projects.libraries.testtags) - implementation(projects.libraries.uiStrings) - - ksp(libs.showkase.processor) - implementation(libs.showkase) - - testCommonDependencies(libs) - } +} + +dependencies { + api(projects.libraries.compound) + + implementation(libs.androidx.compose.material3.windowsizeclass) + implementation(libs.androidx.compose.material3.adaptive) + implementation(libs.coil.compose) + implementation(libs.vanniktech.blurhash) + implementation(projects.libraries.androidutils) + implementation(projects.libraries.architecture) + implementation(projects.libraries.core) + implementation(projects.libraries.preferences.api) + implementation(projects.libraries.testtags) + implementation(projects.libraries.uiStrings) + + ksp(libs.showkase.processor) + implementation(libs.showkase) + + testCommonDependencies(libs) } 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 314d4fef7c..151922213b 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 @@ -114,8 +114,7 @@ enum class FeatureFlags( title = "Sync notifications with WorkManager", description = "Use WorkManager to schedule notification sync tasks when a push is received." + " This should improve reliability and battery usage.", - // Enable by default on nightly and debug builds so we can get feedback before enabling it for everyone. - defaultValue = { meta -> meta.buildType != BuildType.RELEASE }, + defaultValue = { true }, isFinished = false, ), } diff --git a/libraries/featureflag/test/build.gradle.kts b/libraries/featureflag/test/build.gradle.kts index e2920a07b7..f2361417a0 100644 --- a/libraries/featureflag/test/build.gradle.kts +++ b/libraries/featureflag/test/build.gradle.kts @@ -11,11 +11,11 @@ plugins { android { namespace = "io.element.android.libraries.featureflag.test" - - dependencies { - api(projects.libraries.featureflag.api) - implementation(projects.libraries.core) - implementation(projects.libraries.matrix.test) - implementation(libs.coroutines.core) - } +} + +dependencies { + api(projects.libraries.featureflag.api) + implementation(projects.libraries.core) + implementation(projects.libraries.matrix.test) + implementation(libs.coroutines.core) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/HomeServerLoginCompatibilityChecker.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/HomeServerLoginCompatibilityChecker.kt new file mode 100644 index 0000000000..aec1665455 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/HomeServerLoginCompatibilityChecker.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.api.auth + +/** + * Checks the homeserver's compatibility with Element X. + */ +interface HomeServerLoginCompatibilityChecker { + /** + * Performs the compatibility check given the homeserver's [url]. + * @return a `true` value if the homeserver is compatible, `false` if not, or a failure result if the check unexpectedly failed. + */ + suspend fun check(url: String): Result +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index 35b5bcf2b9..1882b04021 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -33,6 +33,7 @@ import org.matrix.rustcomponents.sdk.RequestConfig import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.SlidingSyncVersion import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder +import org.matrix.rustcomponents.sdk.SqliteStoreBuilder import org.matrix.rustcomponents.sdk.use import timber.log.Timber import uniffi.matrix_sdk_crypto.CollectStrategy @@ -105,12 +106,13 @@ class RustMatrixClientFactory( slidingSyncType: ClientBuilderSlidingSync, ): ClientBuilder { return clientBuilderProvider.provide() - .sessionPaths( - dataPath = sessionPaths.fileDirectory.absolutePath, - cachePath = sessionPaths.cacheDirectory.absolutePath, + .sqliteStore( + SqliteStoreBuilder( + dataPath = sessionPaths.fileDirectory.absolutePath, + cachePath = sessionPaths.cacheDirectory.absolutePath, + ).passphrase(passphrase) ) .setSessionDelegate(sessionDelegate) - .sessionPassphrase(passphrase) .userAgent(userAgentProvider.provide()) .addRootCertificates(userCertificatesProvider.provides()) .autoEnableBackups(true) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt new file mode 100644 index 0000000000..8cfaca077b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeServerLoginCompatibilityChecker.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.matrix.impl.auth + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import dev.zacsweers.metro.Inject +import io.element.android.libraries.core.extensions.runCatchingExceptions +import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker +import io.element.android.libraries.matrix.impl.ClientBuilderProvider +import timber.log.Timber + +@ContributesBinding(AppScope::class) +@Inject +class RustHomeServerLoginCompatibilityChecker( + private val clientBuilderProvider: ClientBuilderProvider, +) : HomeServerLoginCompatibilityChecker { + override suspend fun check(url: String): Result = runCatchingExceptions { + clientBuilderProvider.provide() + .inMemoryStore() + .serverNameOrHomeserverUrl(url) + .build() + .use { + it.homeserverLoginDetails() + } + .use { + Timber.d("Homeserver $url | OIDC: ${it.supportsOidcLogin()} | Password: ${it.supportsPasswordLogin()} | SSO: ${it.supportsSsoLogin()}") + it.supportsOidcLogin() || it.supportsPasswordLogin() + } + } +} 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 408af2eb0a..9859358ae6 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 @@ -285,7 +285,6 @@ class RustMatrixAuthenticationService( runCatchingExceptions { val client = makeQrCodeLoginClient( sessionPaths = emptySessionPaths, - passphrase = pendingPassphrase, qrCodeData = sdkQrCodeLoginData, ) client.loginWithQrCode( @@ -344,7 +343,6 @@ class RustMatrixAuthenticationService( private suspend fun makeQrCodeLoginClient( sessionPaths: SessionPaths, - passphrase: String?, qrCodeData: QrCodeData, ): Client { Timber.d("Creating client for QR Code login with simplified sliding sync") @@ -354,7 +352,6 @@ class RustMatrixAuthenticationService( passphrase = pendingPassphrase, slidingSyncType = ClientBuilderSlidingSync.Discovered, ) - .sessionPassphrase(passphrase) .serverNameOrHomeserverUrl(qrCodeData.serverName()!!) .build() } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index bad622e456..7e06eb3c75 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -39,6 +39,7 @@ class NotificationMapper( isDirect = item.roomInfo.isDirect, activeMembersCount = item.roomInfo.joinedMembersCount.toInt(), ) + val timestamp = item.timestamp() ?: clock.epochMillis() NotificationData( sessionId = sessionId, eventId = eventId, @@ -53,8 +54,8 @@ class NotificationMapper( isDm = isDm, isEncrypted = item.roomInfo.isEncrypted.orFalse(), isNoisy = item.isNoisy.orFalse(), - timestamp = item.timestamp() ?: clock.epochMillis(), - content = item.event.use { notificationContentMapper.map(it) }.getOrThrow(), + timestamp = timestamp, + content = notificationContentMapper.map(item.event).getOrThrow(), hasMention = item.hasMention.orFalse(), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt index 2ca4a3c823..b2952461df 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventToNotificationContentMapper.kt @@ -25,8 +25,9 @@ class TimelineEventToNotificationContentMapper { fun map(timelineEvent: TimelineEvent): Result { return runCatchingExceptions { timelineEvent.use { + val senderId = UserId(timelineEvent.senderId()) timelineEvent.eventType().use { eventType -> - eventType.toContent(senderId = UserId(timelineEvent.senderId())) + eventType.toContent(senderId = senderId) } } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.kt new file mode 100644 index 0000000000..4557d47f5b --- /dev/null +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/auth/RustHomeserverLoginCompatibilityCheckerTest.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.libraries.matrix.impl.auth + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.impl.FakeClientBuilderProvider +import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClient +import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiClientBuilder +import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiHomeserverLoginDetails +import kotlinx.coroutines.test.runTest +import org.junit.Ignore +import org.junit.Test + +@Ignore("JNA direct mapping has broken unit tests with FFI fakes") +class RustHomeserverLoginCompatibilityCheckerTest { + @Test + fun `check - is valid if it supports OIDC login`() = runTest { + val sut = createChecker { FakeFfiHomeserverLoginDetails(supportsOidcLogin = true) } + assertThat(sut.check("https://matrix.host.org").getOrNull()).isTrue() + } + + @Test + fun `check - is valid if it supports password login`() = runTest { + val sut = createChecker { FakeFfiHomeserverLoginDetails(supportsPasswordLogin = true) } + assertThat(sut.check("https://matrix.host.org").getOrNull()).isTrue() + } + + @Test + fun `check - is not valid if it only supports SSO login`() = runTest { + val sut = createChecker { FakeFfiHomeserverLoginDetails(supportsSsoLogin = true) } + assertThat(sut.check("https://matrix.host.org").getOrNull()).isFalse() + } + + @Test + fun `check - is not valid if fetching the data fails`() = runTest { + val sut = createChecker { error("Unexpected error!") } + assertThat(sut.check("https://matrix.host.org").isFailure).isTrue() + } + + private fun createChecker( + result: () -> FakeFfiHomeserverLoginDetails, + ) = RustHomeServerLoginCompatibilityChecker( + clientBuilderProvider = FakeClientBuilderProvider { + FakeFfiClientBuilder { + FakeFfiClient(homeserverLoginDetailsResult = result) + } + } + ) +} diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt index 6cff38f21a..81dbfafe5e 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiClientBuilder.kt @@ -13,6 +13,7 @@ import org.matrix.rustcomponents.sdk.ClientSessionDelegate import org.matrix.rustcomponents.sdk.NoHandle import org.matrix.rustcomponents.sdk.RequestConfig import org.matrix.rustcomponents.sdk.SlidingSyncVersionBuilder +import org.matrix.rustcomponents.sdk.SqliteStoreBuilder import uniffi.matrix_sdk.BackupDownloadStrategy import uniffi.matrix_sdk_crypto.CollectStrategy import uniffi.matrix_sdk_crypto.DecryptionSettings @@ -29,7 +30,6 @@ class FakeFfiClientBuilder( override fun decryptionSettings(decryptionSettings: DecryptionSettings): ClientBuilder = this override fun disableSslVerification() = this override fun homeserverUrl(url: String) = this - override fun sessionPassphrase(passphrase: String?) = this override fun proxy(url: String) = this override fun requestConfig(config: RequestConfig) = this override fun roomKeyRecipientStrategy(strategy: CollectStrategy) = this @@ -42,5 +42,6 @@ class FakeFfiClientBuilder( override fun username(username: String) = this override fun enableShareHistoryOnInvite(enableShareHistoryOnInvite: Boolean): ClientBuilder = this override fun threadsEnabled(enabled: Boolean, threadSubscriptions: Boolean): ClientBuilder = this + override fun sqliteStore(config: SqliteStoreBuilder): ClientBuilder = this override suspend fun build() = buildResult() } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt index 85328a2d4d..7951309bcd 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/fakes/FakeFfiHomeserverLoginDetails.kt @@ -12,10 +12,12 @@ import org.matrix.rustcomponents.sdk.NoHandle class FakeFfiHomeserverLoginDetails( private val url: String = "https://example.org", - private val supportsPasswordLogin: Boolean = true, - private val supportsOidcLogin: Boolean = false + private val supportsPasswordLogin: Boolean = false, + private val supportsOidcLogin: Boolean = false, + private val supportsSsoLogin: Boolean = false, ) : HomeserverLoginDetails(NoHandle) { override fun url(): String = url override fun supportsOidcLogin(): Boolean = supportsOidcLogin override fun supportsPasswordLogin(): Boolean = supportsPasswordLogin + override fun supportsSsoLogin(): Boolean = supportsSsoLogin } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeHomeServerLoginCompatibilityChecker.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeHomeServerLoginCompatibilityChecker.kt new file mode 100644 index 0000000000..934784b684 --- /dev/null +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeHomeServerLoginCompatibilityChecker.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.matrix.test.auth + +import io.element.android.libraries.matrix.api.auth.HomeServerLoginCompatibilityChecker + +class FakeHomeServerLoginCompatibilityChecker( + private val checkResult: (String) -> Result, +) : HomeServerLoginCompatibilityChecker { + override suspend fun check(url: String): Result { + return checkResult(url) + } +} diff --git a/libraries/mediapickers/api/build.gradle.kts b/libraries/mediapickers/api/build.gradle.kts index c130cd7900..aaf9b4cd0c 100644 --- a/libraries/mediapickers/api/build.gradle.kts +++ b/libraries/mediapickers/api/build.gradle.kts @@ -13,12 +13,12 @@ plugins { android { namespace = "io.element.android.libraries.mediapickers.api" - - dependencies { - implementation(projects.libraries.uiStrings) - implementation(projects.libraries.core) - implementation(projects.libraries.di) - - testCommonDependencies(libs) - } +} + +dependencies { + implementation(projects.libraries.uiStrings) + implementation(projects.libraries.core) + implementation(projects.libraries.di) + + testCommonDependencies(libs) } diff --git a/libraries/mediapickers/test/build.gradle.kts b/libraries/mediapickers/test/build.gradle.kts index ee743bb63d..dc37741b02 100644 --- a/libraries/mediapickers/test/build.gradle.kts +++ b/libraries/mediapickers/test/build.gradle.kts @@ -15,10 +15,10 @@ setupDependencyInjection() android { namespace = "io.element.android.libraries.mediapickers.test" - - dependencies { - implementation(projects.libraries.core) - implementation(projects.libraries.di) - api(projects.libraries.mediapickers.api) - } +} + +dependencies { + implementation(projects.libraries.core) + implementation(projects.libraries.di) + api(projects.libraries.mediapickers.api) } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaGalleryEntryPoint.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaGalleryEntryPoint.kt index 119c1002c8..2fdcaf1bd0 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaGalleryEntryPoint.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaGalleryEntryPoint.kt @@ -14,16 +14,15 @@ import io.element.android.libraries.architecture.FeatureEntryPoint import io.element.android.libraries.matrix.api.core.EventId interface MediaGalleryEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { fun onBackClick() - fun onViewInTimeline(eventId: EventId) - fun forwardEvent(eventId: EventId) + fun viewInTimeline(eventId: EventId) + fun forward(eventId: EventId, fromPinnedEvents: Boolean) } } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt index b16de69b98..ab1c78abea 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/MediaViewerEntryPoint.kt @@ -19,19 +19,19 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import kotlinx.parcelize.Parcelize interface MediaViewerEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun params(params: Params): NodeBuilder - fun avatar(filename: String, avatarUrl: String): NodeBuilder - fun build(): Node - } + fun createParamsForAvatar(filename: String, avatarUrl: String): Params interface Callback : Plugin { fun onDone() - fun onViewInTimeline(eventId: EventId) - fun onForwardEvent(eventId: EventId) + fun viewInTimeline(eventId: EventId) + fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) } data class Params( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt index 75686b637a..60935a204c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.mediaviewer.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.architecture.createNode @@ -18,18 +17,14 @@ import io.element.android.libraries.mediaviewer.impl.gallery.root.MediaGalleryFl @ContributesBinding(AppScope::class) class DefaultMediaGalleryEntryPoint : MediaGalleryEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MediaGalleryEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : MediaGalleryEntryPoint.NodeBuilder { - override fun callback(callback: MediaGalleryEntryPoint.Callback): MediaGalleryEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: MediaGalleryEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(callback), + ) } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt index da7c797bfb..7d683bf3df 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.mediaviewer.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.architecture.createNode @@ -22,52 +21,42 @@ import io.element.android.libraries.mediaviewer.impl.viewer.MediaViewerNode @ContributesBinding(AppScope::class) class DefaultMediaViewerEntryPoint : MediaViewerEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MediaViewerEntryPoint.NodeBuilder { - val plugins = ArrayList() + override fun createParamsForAvatar(filename: String, avatarUrl: String): MediaViewerEntryPoint.Params { + // We need to fake the MimeType here for the viewer to work. + val mimeType = MimeTypes.Images + return MediaViewerEntryPoint.Params( + mode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia, + eventId = null, + mediaInfo = MediaInfo( + filename = filename, + fileSize = null, + caption = null, + mimeType = mimeType, + formattedFileSize = "", + fileExtension = "", + senderId = UserId("@dummy:server.org"), + senderName = null, + senderAvatar = null, + dateSent = null, + dateSentFull = null, + waveform = null, + duration = null, + ), + mediaSource = MediaSource(url = avatarUrl), + thumbnailSource = null, + canShowInfo = false, + ) + } - return object : MediaViewerEntryPoint.NodeBuilder { - override fun callback(callback: MediaViewerEntryPoint.Callback): MediaViewerEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun params(params: MediaViewerEntryPoint.Params): MediaViewerEntryPoint.NodeBuilder { - plugins += params - return this - } - - override fun avatar(filename: String, avatarUrl: String): MediaViewerEntryPoint.NodeBuilder { - // We need to fake the MimeType here for the viewer to work. - val mimeType = MimeTypes.Images - return params( - MediaViewerEntryPoint.Params( - mode = MediaViewerEntryPoint.MediaViewerMode.SingleMedia, - eventId = null, - mediaInfo = MediaInfo( - filename = filename, - fileSize = null, - caption = null, - mimeType = mimeType, - formattedFileSize = "", - fileExtension = "", - senderId = UserId("@dummy:server.org"), - senderName = null, - senderAvatar = null, - dateSent = null, - dateSentFull = null, - waveform = null, - duration = null, - ), - mediaSource = MediaSource(url = avatarUrl), - thumbnailSource = null, - canShowInfo = false, - ) - ) - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: MediaViewerEntryPoint.Params, + callback: MediaViewerEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf(params, callback), + ) } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt index 6ee31c6520..d784f972d0 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/MediaGalleryNode.kt @@ -13,10 +13,10 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.mediaviewer.impl.gallery.di.LocalMediaItemPresenterFactories @@ -38,33 +38,19 @@ class MediaGalleryNode( interface Callback : Plugin { fun onBackClick() - fun onItemClick(item: MediaItem.Event) - fun onViewInTimeline(eventId: EventId) - fun onForward(eventId: EventId) + fun showItem(item: MediaItem.Event) + fun viewInTimeline(eventId: EventId) + fun forward(eventId: EventId) } - private fun onBackClick() { - plugins().forEach { - it.onBackClick() - } - } + private val callback: Callback = callback() override fun onViewInTimelineClick(eventId: EventId) { - plugins().forEach { - it.onViewInTimeline(eventId) - } + callback.viewInTimeline(eventId) } override fun onForwardClick(eventId: EventId) { - plugins().forEach { - it.onForward(eventId) - } - } - - private fun onItemClick(item: MediaItem.Event) { - plugins().forEach { - it.onItemClick(item) - } + callback.forward(eventId) } @Composable @@ -75,8 +61,8 @@ class MediaGalleryNode( val state = presenter.present() MediaGalleryView( state = state, - onBackClick = ::onBackClick, - onItemClick = ::onItemClick, + onBackClick = callback::onBackClick, + onItemClick = callback::showItem, modifier = modifier, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt index 76f025006f..e21da04586 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/gallery/root/MediaGalleryFlowNode.kt @@ -13,13 +13,13 @@ 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 com.bumble.appyx.navmodel.backstack.BackStack import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.BackstackWithOverlayBox import io.element.android.libraries.architecture.BaseFlowNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.overlay.Overlay import io.element.android.libraries.architecture.overlay.operation.hide @@ -70,41 +70,25 @@ class MediaGalleryFlowNode( ) : NavTarget } - private fun onBackClick() { - plugins().forEach { - it.onBackClick() - } - } - - private fun onViewInTimeline(eventId: EventId) { - plugins().forEach { - it.onViewInTimeline(eventId) - } - } - - private fun forwardEvent(eventId: EventId) { - plugins().forEach { - it.forwardEvent(eventId) - } - } + private val callback: MediaGalleryEntryPoint.Callback = callback() override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { return when (navTarget) { NavTarget.Root -> { val callback = object : MediaGalleryNode.Callback { override fun onBackClick() { - this@MediaGalleryFlowNode.onBackClick() + callback.onBackClick() } - override fun onViewInTimeline(eventId: EventId) { - this@MediaGalleryFlowNode.onViewInTimeline(eventId) + override fun viewInTimeline(eventId: EventId) { + callback.viewInTimeline(eventId) } - override fun onForward(eventId: EventId) { - forwardEvent(eventId) + override fun forward(eventId: EventId) { + callback.forward(eventId, fromPinnedEvents = false) } - override fun onItemClick(item: MediaItem.Event) { + override fun showItem(item: MediaItem.Event) { val mode = when (item) { is MediaItem.Audio, is MediaItem.Voice, @@ -131,28 +115,28 @@ class MediaGalleryFlowNode( overlay.hide() } - override fun onViewInTimeline(eventId: EventId) { - this@MediaGalleryFlowNode.onViewInTimeline(eventId) + override fun viewInTimeline(eventId: EventId) { + callback.viewInTimeline(eventId) } - override fun onForwardEvent(eventId: EventId) { + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) { // Need to go to the parent because of the overlay - forwardEvent(eventId) + callback.forward(eventId, fromPinnedEvents) } } - mediaViewerEntryPoint.nodeBuilder(this, buildContext) - .params( - MediaViewerEntryPoint.Params( - mode = navTarget.mode, - eventId = navTarget.eventId, - mediaInfo = navTarget.mediaInfo, - mediaSource = navTarget.mediaSource, - thumbnailSource = navTarget.thumbnailSource, - canShowInfo = true, - ) - ) - .callback(callback) - .build() + mediaViewerEntryPoint.createNode( + parentNode = this, + buildContext = buildContext, + params = MediaViewerEntryPoint.Params( + mode = navTarget.mode, + eventId = navTarget.eventId, + mediaInfo = navTarget.mediaInfo, + mediaSource = navTarget.mediaSource, + thumbnailSource = navTarget.thumbnailSource, + canShowInfo = true, + ), + callback = callback, + ) } } } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNavigator.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNavigator.kt index 77e253dfa5..b3fa321170 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNavigator.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerNavigator.kt @@ -11,6 +11,6 @@ import io.element.android.libraries.matrix.api.core.EventId interface MediaViewerNavigator { fun onViewInTimelineClick(eventId: EventId) - fun onForwardClick(eventId: EventId) + fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean) fun onItemDeleted() } 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 ee874156cd..f47f341657 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 @@ -15,7 +15,6 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode @@ -23,6 +22,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.compound.theme.ForcedDarkElementTheme import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.viewfolder.api.TextFileViewer +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.core.coroutine.CoroutineDispatchers @@ -57,28 +57,19 @@ class MediaViewerNode( private val enterpriseService: EnterpriseService, ) : Node(buildContext, plugins = plugins), MediaViewerNavigator { + private val callback: MediaViewerEntryPoint.Callback = callback() private val inputs = inputs() - private fun onDone() { - plugins().forEach { - it.onDone() - } - } - override fun onViewInTimelineClick(eventId: EventId) { - plugins().forEach { - it.onViewInTimeline(eventId) - } + callback.viewInTimeline(eventId) } - override fun onForwardClick(eventId: EventId) { - plugins().forEach { - it.onForwardEvent(eventId) - } + override fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean) { + callback.forwardEvent(eventId, fromPinnedEvents) } override fun onItemDeleted() { - onDone() + callback.onDone() } private val mediaGallerySource = if (inputs.mode == MediaViewerEntryPoint.MediaViewerMode.SingleMedia) { @@ -90,11 +81,7 @@ class MediaViewerNode( timelineMediaGalleryDataSource } else { // Can we use a specific timeline? - val timelineMode = when (val mode = inputs.mode) { - is MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> mode.timelineMode - is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> mode.timelineMode - else -> null - } + val timelineMode = inputs.mode.getTimelineMode() when (timelineMode) { null -> timelineMediaGalleryDataSource Timeline.Mode.Live, @@ -153,8 +140,16 @@ class MediaViewerNode( textFileViewer = textFileViewer, modifier = modifier, audioFocus = audioFocus, - onBackClick = ::onDone, + onBackClick = callback::onDone, ) } } } + +internal fun MediaViewerEntryPoint.MediaViewerMode.getTimelineMode(): Timeline.Mode? { + return when (this) { + is MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> timelineMode + is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> timelineMode + else -> null + } +} 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 726e9989ce..a04a1a370a 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 @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.JoinedRoom 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.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.mediaviewer.api.local.LocalMedia @@ -119,7 +120,10 @@ class MediaViewerPresenter( } is MediaViewerEvents.Forward -> { mediaBottomSheetState = MediaBottomSheetState.Hidden - navigator.onForwardClick(event.eventId) + navigator.onForwardClick( + eventId = event.eventId, + fromPinnedEvents = inputs.mode.getTimelineMode() == Timeline.Mode.PinnedEvents, + ) } is MediaViewerEvents.OpenInfo -> coroutineScope.launch { mediaBottomSheetState = MediaBottomSheetState.MediaDetailsBottomSheetState( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt index 94ac0fea21..31964a6629 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/SingleMediaGalleryDataSource.kt @@ -27,6 +27,7 @@ class SingleMediaGalleryDataSource( override fun start() = Unit override fun groupedMediaItemsFlow() = flowOf(AsyncData.Success(data)) override fun getLastData(): AsyncData = AsyncData.Success(data) + override suspend fun loadMore(direction: Timeline.PaginationDirection) = Unit override suspend fun deleteItem(eventId: EventId) = Unit diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPointTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPointTest.kt index 38040426e1..991e29cd98 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPointTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaGalleryEntryPointTest.kt @@ -9,13 +9,12 @@ package io.element.android.libraries.mediaviewer.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext -import com.bumble.appyx.core.node.Node import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint -import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.mediaviewer.impl.gallery.root.MediaGalleryFlowNode +import io.element.android.libraries.mediaviewer.test.FakeMediaViewerEntryPoint import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.node.TestParentNode import org.junit.Rule @@ -35,19 +34,19 @@ class DefaultMediaGalleryEntryPointTest { MediaGalleryFlowNode( buildContext = buildContext, plugins = plugins, - mediaViewerEntryPoint = object : MediaViewerEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext) = lambdaError() - }, + mediaViewerEntryPoint = FakeMediaViewerEntryPoint(), ) } val callback = object : MediaGalleryEntryPoint.Callback { override fun onBackClick() = lambdaError() - override fun onViewInTimeline(eventId: EventId) = lambdaError() - override fun forwardEvent(eventId: EventId) = lambdaError() + override fun viewInTimeline(eventId: EventId) = lambdaError() + override fun forward(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(MediaGalleryFlowNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt index a1f60cc124..f6246545b5 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt @@ -71,14 +71,16 @@ class DefaultMediaViewerEntryPointTest { } val callback = object : MediaViewerEntryPoint.Callback { override fun onDone() = lambdaError() - override fun onViewInTimeline(eventId: EventId) = lambdaError() - override fun onForwardEvent(eventId: EventId) = lambdaError() + override fun viewInTimeline(eventId: EventId) = lambdaError() + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() } val params = createMediaViewerEntryPointParams() - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(MediaViewerNode::class.java) assertThat(result.plugins).contains(params) assertThat(result.plugins).contains(callback) @@ -115,16 +117,19 @@ class DefaultMediaViewerEntryPointTest { } val callback = object : MediaViewerEntryPoint.Callback { override fun onDone() = lambdaError() - override fun onViewInTimeline(eventId: EventId) = lambdaError() - override fun onForwardEvent(eventId: EventId) = lambdaError() + override fun viewInTimeline(eventId: EventId) = lambdaError() + override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .avatar( - filename = "fn", - avatarUrl = "avatarUrl", - ) - .callback(callback) - .build() + val params = entryPoint.createParamsForAvatar( + filename = "fn", + avatarUrl = "avatarUrl", + ) + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(MediaViewerNode::class.java) assertThat(result.plugins).contains( MediaViewerEntryPoint.Params( diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/FakeMediaViewerNavigator.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/FakeMediaViewerNavigator.kt index 791527445b..d223ad1ce9 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/FakeMediaViewerNavigator.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/FakeMediaViewerNavigator.kt @@ -12,15 +12,15 @@ import io.element.android.tests.testutils.lambda.lambdaError class FakeMediaViewerNavigator( private val onViewInTimelineClickLambda: (EventId) -> Unit = { lambdaError() }, - private val onForwardClickLambda: (EventId) -> Unit = { lambdaError() }, + private val onForwardClickLambda: (EventId, Boolean) -> Unit = { _, _ -> lambdaError() }, private val onItemDeletedLambda: () -> Unit = { lambdaError() }, ) : MediaViewerNavigator { override fun onViewInTimelineClick(eventId: EventId) { onViewInTimelineClickLambda(eventId) } - override fun onForwardClick(eventId: EventId) { - onForwardClickLambda(eventId) + override fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean) { + onForwardClickLambda(eventId, fromPinnedEvents) } override fun onItemDeleted() { diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index 359eda7a5e..f6de72b2d8 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -785,7 +785,7 @@ class MediaViewerPresenterTest { @Test fun `present - forward hides the bottom sheet and invokes the navigator`() = runTest { - val onForwardClickLambda = lambdaRecorder { } + val onForwardClickLambda = lambdaRecorder { _, _ -> } val navigator = FakeMediaViewerNavigator( onForwardClickLambda = onForwardClickLambda, ) @@ -804,7 +804,35 @@ class MediaViewerPresenterTest { initialState.eventSink(MediaViewerEvents.Forward(AN_EVENT_ID)) val finalState = awaitItem() assertThat(finalState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) - onForwardClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID)) + onForwardClickLambda.assertions().isCalledOnce() + .with(value(AN_EVENT_ID), value(false)) + } + } + + @Test + fun `present - forward from pinned events hides the bottom sheet and invokes the navigator`() = runTest { + val onForwardClickLambda = lambdaRecorder { _, _ -> } + val navigator = FakeMediaViewerNavigator( + onForwardClickLambda = onForwardClickLambda, + ) + val presenter = createMediaViewerPresenter( + mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.PinnedEvents), + localMediaFactory = localMediaFactory, + mediaViewerNavigator = navigator, + room = FakeJoinedRoom( + baseRoom = FakeBaseRoom(canRedactOwnResult = { Result.success(true) }), + ), + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink(MediaViewerEvents.OpenInfo(aMediaViewerPageData())) + val withBottomSheetState = awaitItem() + assertThat(withBottomSheetState.mediaBottomSheetState).isInstanceOf(MediaBottomSheetState.MediaDetailsBottomSheetState::class.java) + initialState.eventSink(MediaViewerEvents.Forward(AN_EVENT_ID)) + val finalState = awaitItem() + assertThat(finalState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) + onForwardClickLambda.assertions().isCalledOnce() + .with(value(AN_EVENT_ID), value(true)) } } diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaGalleryEntryPoint.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaGalleryEntryPoint.kt new file mode 100644 index 0000000000..9ba6bfee62 --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaGalleryEntryPoint.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.mediaviewer.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeMediaGalleryEntryPoint : MediaGalleryEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: MediaGalleryEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaViewerEntryPoint.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaViewerEntryPoint.kt new file mode 100644 index 0000000000..9890169579 --- /dev/null +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/FakeMediaViewerEntryPoint.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.mediaviewer.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeMediaViewerEntryPoint : MediaViewerEntryPoint { + override fun createParamsForAvatar(filename: String, avatarUrl: String) = lambdaError() + + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: MediaViewerEntryPoint.Params, + callback: MediaViewerEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt index f12d92a50d..c6bfbd73ed 100644 --- a/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt +++ b/libraries/permissions/impl/src/main/kotlin/io/element/android/libraries/permissions/impl/DefaultPermissionsPresenter.kt @@ -125,7 +125,7 @@ class DefaultPermissionsPresenter( showDialog = showDialog.value, permissionAlreadyAsked = isAlreadyAsked, permissionAlreadyDenied = isAlreadyDenied, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } diff --git a/libraries/preferences/test/build.gradle.kts b/libraries/preferences/test/build.gradle.kts index 9db4ab1866..44116aac13 100644 --- a/libraries/preferences/test/build.gradle.kts +++ b/libraries/preferences/test/build.gradle.kts @@ -11,12 +11,12 @@ plugins { android { namespace = "io.element.android.libraries.preferences.test" - - dependencies { - api(projects.libraries.preferences.api) - implementation(projects.libraries.matrix.api) - implementation(projects.tests.testutils) - implementation(libs.coroutines.core) - implementation(libs.androidx.datastore.preferences) - } +} + +dependencies { + api(projects.libraries.preferences.api) + implementation(projects.libraries.matrix.api) + implementation(projects.tests.testutils) + implementation(libs.coroutines.core) + implementation(libs.androidx.datastore.preferences) } diff --git a/libraries/previewutils/build.gradle.kts b/libraries/previewutils/build.gradle.kts index 92218e9286..111cb4c830 100644 --- a/libraries/previewutils/build.gradle.kts +++ b/libraries/previewutils/build.gradle.kts @@ -11,11 +11,11 @@ plugins { android { namespace = "io.element.android.libraries.previewutils" - - dependencies { - implementation(projects.libraries.designsystem) - implementation(projects.libraries.matrix.api) - - implementation(libs.kotlinx.collections.immutable) - } +} + +dependencies { + implementation(projects.libraries.designsystem) + implementation(projects.libraries.matrix.api) + + implementation(libs.kotlinx.collections.immutable) } diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 2ea597b4f2..2de79ab456 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -21,6 +21,12 @@ android { isIncludeAndroidResources = true } } + + buildTypes { + register("nightly") { + matchingFallbacks += listOf("release") + } + } } setupDependencyInjection() diff --git a/libraries/push/impl/src/debug/res/raw/message.mp3 b/libraries/push/impl/src/debug/res/raw/message.mp3 new file mode 100644 index 0000000000..abc056786c Binary files /dev/null and b/libraries/push/impl/src/debug/res/raw/message.mp3 differ diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt index 93e855c94d..e753e4d9e0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTest.kt @@ -62,7 +62,7 @@ class IgnoredUsersTest( coroutineScope: CoroutineScope, navigator: NotificationTroubleshootNavigator, ) { - navigator.openIgnoredUsers() + navigator.navigateToBlockedUsers() } override suspend fun reset() = delegate.reset() diff --git a/libraries/push/impl/src/nightly/res/raw/message.mp3 b/libraries/push/impl/src/nightly/res/raw/message.mp3 new file mode 100644 index 0000000000..abc056786c Binary files /dev/null and b/libraries/push/impl/src/nightly/res/raw/message.mp3 differ diff --git a/libraries/push/impl/src/main/res/raw/message.mp3 b/libraries/push/impl/src/release/res/raw/message.mp3 similarity index 100% rename from libraries/push/impl/src/main/res/raw/message.mp3 rename to libraries/push/impl/src/release/res/raw/message.mp3 diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt index e38baa7321..eebfd9d15b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/troubleshoot/IgnoredUsersTestTest.kt @@ -39,7 +39,7 @@ class IgnoredUsersTestTest { ) val openIgnoredUsersResult = lambdaRecorder {} val navigator = object : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = openIgnoredUsersResult() + override fun navigateToBlockedUsers() = openIgnoredUsersResult() } sut.quickFix( coroutineScope = backgroundScope, diff --git a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt index a5d13609bd..1da6c4dd4c 100644 --- a/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt +++ b/libraries/roomselect/api/src/main/kotlin/io/element/android/libraries/roomselect/api/RoomSelectEntryPoint.kt @@ -18,12 +18,12 @@ interface RoomSelectEntryPoint : FeatureEntryPoint { val mode: RoomSelectMode, ) - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - interface NodeBuilder { - fun params(params: Params): NodeBuilder - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: Params, + callback: Callback, + ): Node interface Callback : Plugin { fun onRoomSelected(roomIds: List) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt index db17e3bd31..94488ec1c4 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.roomselect.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.SessionScope @@ -17,23 +16,18 @@ import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint @ContributesBinding(SessionScope::class) class DefaultRoomSelectEntryPoint : RoomSelectEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): RoomSelectEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : RoomSelectEntryPoint.NodeBuilder { - override fun params(params: RoomSelectEntryPoint.Params): RoomSelectEntryPoint.NodeBuilder { - plugins += RoomSelectNode.Inputs(mode = params.mode) - return this - } - - override fun callback(callback: RoomSelectEntryPoint.Callback): RoomSelectEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: RoomSelectEntryPoint.Params, + callback: RoomSelectEntryPoint.Callback, + ): Node { + return parentNode.createNode( + buildContext = buildContext, + plugins = listOf( + RoomSelectNode.Inputs(mode = params.mode), + callback, + ) + ) } } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt index a7e6dc0cb0..5c05dcf6ec 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectNode.kt @@ -16,9 +16,9 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint import io.element.android.libraries.roomselect.api.RoomSelectMode @@ -35,24 +35,15 @@ class RoomSelectNode( private val inputs: Inputs = inputs() private val presenter = presenterFactory.create(inputs.mode) - - private val callbacks = plugins.filterIsInstance() - - private fun onDismiss() { - callbacks.forEach { it.onCancel() } - } - - private fun onSubmit(roomIds: List) { - callbacks.forEach { it.onRoomSelected(roomIds) } - } + private val callback: RoomSelectEntryPoint.Callback = callback() @Composable override fun View(modifier: Modifier) { val state = presenter.present() RoomSelectView( state = state, - onDismiss = ::onDismiss, - onSubmit = ::onSubmit, + onDismiss = callback::onCancel, + onSubmit = callback::onRoomSelected, modifier = modifier ) } diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt index ffd3fda5c0..841b955018 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectPresenter.kt @@ -87,7 +87,7 @@ class RoomSelectPresenter( query = searchQuery, isSearchActive = isSearchActive, selectedRooms = selectedRooms, - eventSink = { handleEvents(it) } + eventSink = ::handleEvents, ) } } diff --git a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt index bb9c15e7e6..0bd16f77b0 100644 --- a/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt +++ b/libraries/roomselect/impl/src/test/kotlin/io/element/android/libraries/roomselect/impl/DefaultRoomSelectEntryPointTest.kt @@ -42,10 +42,12 @@ class DefaultRoomSelectEntryPointTest { override fun onCancel() = lambdaError() } val params = RoomSelectEntryPoint.Params(testMode) - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .params(params) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + params = params, + callback = callback, + ) assertThat(result).isInstanceOf(RoomSelectNode::class.java) assertThat(result.plugins).contains(RoomSelectNode.Inputs(params.mode)) assertThat(result.plugins).contains(callback) diff --git a/libraries/roomselect/test/build.gradle.kts b/libraries/roomselect/test/build.gradle.kts new file mode 100644 index 0000000000..c4b63ef3c4 --- /dev/null +++ b/libraries/roomselect/test/build.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +plugins { + id("io.element.android-library") +} + +android { + namespace = "io.element.android.libraries.roomselect.test" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(projects.libraries.roomselect.api) + implementation(projects.tests.testutils) +} diff --git a/libraries/roomselect/test/src/main/kotlin/io/element/android/libraries/roomselect/test/FakeRoomSelectEntryPoint.kt b/libraries/roomselect/test/src/main/kotlin/io/element/android/libraries/roomselect/test/FakeRoomSelectEntryPoint.kt new file mode 100644 index 0000000000..5c7e4d11d8 --- /dev/null +++ b/libraries/roomselect/test/src/main/kotlin/io/element/android/libraries/roomselect/test/FakeRoomSelectEntryPoint.kt @@ -0,0 +1,22 @@ +/* + * 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.roomselect.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeRoomSelectEntryPoint : RoomSelectEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + params: RoomSelectEntryPoint.Params, + callback: RoomSelectEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt index 7900b4d90d..f3b66d73c0 100644 --- a/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt +++ b/libraries/session-storage/api/src/main/kotlin/io/element/android/libraries/sessionstorage/api/SessionStore.kt @@ -50,6 +50,11 @@ interface SessionStore { */ suspend fun getAllSessions(): List + /** + * Get the number of sessions. + */ + suspend fun numberOfSessions(): Int + /** * Get the latest session, or null if no session exists. */ diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt index 0560d3076a..81353f5305 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt @@ -161,6 +161,15 @@ class DatabaseSessionStore( } } + override suspend fun numberOfSessions(): Int { + return sessionDataMutex.withLock { + database.sessionDataQueries.count() + .executeAsOneOrNull() + ?.toInt() + ?: 0 + } + } + override fun sessionsFlow(): Flow> { return database.sessionDataQueries.selectAll() .asFlow() diff --git a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq index 53d07bfba3..b61c746fb8 100644 --- a/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq +++ b/libraries/session-storage/impl/src/main/sqldelight/io/element/android/libraries/matrix/session/SessionData.sq @@ -47,6 +47,9 @@ SELECT * FROM SessionData ORDER BY lastUsageIndex DESC LIMIT 1; selectAll: SELECT * FROM SessionData ORDER BY lastUsageIndex DESC; +count: +SELECT count(*) FROM SessionData; + selectByUserId: SELECT * FROM SessionData WHERE userId = ?; diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt index d1fef8a39a..f28d9e21df 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTest.kt @@ -52,6 +52,7 @@ class DatabaseSessionStoreTest { assertThat(database.sessionDataQueries.selectLatest().executeAsOneOrNull()).isEqualTo(aSessionData) assertThat(database.sessionDataQueries.selectAll().executeAsList().size).isEqualTo(1) + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(1) } @Test @@ -109,6 +110,7 @@ class DatabaseSessionStoreTest { assertThat(foundSession).isEqualTo(aSessionData) assertThat(database.sessionDataQueries.selectAll().executeAsList().size).isEqualTo(2) + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(2) } @Test @@ -196,12 +198,16 @@ class DatabaseSessionStoreTest { position = 1, lastUsageIndex = 1, ) + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(1) databaseSessionStore.addSession(secondSessionData.toApiModel()) assertThat(awaitItem().size).isEqualTo(2) + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(2) databaseSessionStore.removeSession(aSessionData.userId) assertThat(awaitItem().size).isEqualTo(1) + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(1) databaseSessionStore.removeSession(secondSessionData.userId) assertThat(awaitItem()).isEmpty() + assertThat(database.sessionDataQueries.count().executeAsOneOrNull()).isEqualTo(0) } } diff --git a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt index c8f3078e7a..00dd9ad9c0 100644 --- a/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt +++ b/libraries/session-storage/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemorySessionStore.kt @@ -67,6 +67,10 @@ class InMemorySessionStore( return sessionDataListFlow.value } + override suspend fun numberOfSessions(): Int { + return sessionDataListFlow.value.size + } + override suspend fun getLatestSession(): SessionData? { return sessionDataListFlow.value.firstOrNull() } diff --git a/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml b/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml index 602d35363c..fd55fdf9d9 100644 --- a/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml +++ b/libraries/textcomposer/impl/src/main/res/values-fa/translations.xml @@ -2,7 +2,7 @@ "افزودن پیوست" "تغییر وضعیت سیاههٔ گلوله‌ای" - "بستن گزینه‌های قالب‌بندی" + "لغو و بستن قالب‌بندی متن" "تغییر حالت بلوک کد" "افزودن عنوان" "پیام رمزنگاری شده…" diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/NotificationTroubleShootEntryPoint.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/NotificationTroubleShootEntryPoint.kt index bf0c6bb883..7889ba4da3 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/NotificationTroubleShootEntryPoint.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/NotificationTroubleShootEntryPoint.kt @@ -13,15 +13,14 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface NotificationTroubleShootEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() - fun openIgnoredUsers() + fun navigateToBlockedUsers() } } diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt index 0eab9b8e5a..bef39ecdd4 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/PushHistoryEntryPoint.kt @@ -15,15 +15,14 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId interface PushHistoryEntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { fun onDone() - fun navigateTo(roomId: RoomId, eventId: EventId) + fun navigateToEvent(roomId: RoomId, eventId: EventId) } } diff --git a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt index 0cce358072..75c8a83ae7 100644 --- a/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt +++ b/libraries/troubleshoot/api/src/main/kotlin/io/element/android/libraries/troubleshoot/api/test/NotificationTroubleshootNavigator.kt @@ -8,5 +8,5 @@ package io.element.android.libraries.troubleshoot.api.test interface NotificationTroubleshootNavigator { - fun openIgnoredUsers() + fun navigateToBlockedUsers() } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPoint.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPoint.kt index e6af517069..285f08b65a 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPoint.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.troubleshoot.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.architecture.createNode @@ -17,18 +16,11 @@ import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEnt @ContributesBinding(AppScope::class) class DefaultNotificationTroubleShootEntryPoint : NotificationTroubleShootEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NotificationTroubleShootEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : NotificationTroubleShootEntryPoint.NodeBuilder { - override fun callback(callback: NotificationTroubleShootEntryPoint.Callback): NotificationTroubleShootEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: NotificationTroubleShootEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt index 508010a3d6..22ccda67ce 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.SessionScope import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint import io.element.android.libraries.troubleshoot.api.test.NotificationTroubleshootNavigator @@ -31,20 +31,13 @@ class TroubleshootNotificationsNode( factory: TroubleshootNotificationsPresenter.Factory, ) : Node(buildContext, plugins = plugins), NotificationTroubleshootNavigator { + private val callback: NotificationTroubleShootEntryPoint.Callback = callback() private val presenter = factory.create( navigator = this, ) - private fun onDone() { - plugins().forEach { - it.onDone() - } - } - - override fun openIgnoredUsers() { - plugins().forEach { - it.openIgnoredUsers() - } + override fun navigateToBlockedUsers() { + callback.navigateToBlockedUsers() } @Composable @@ -53,7 +46,7 @@ class TroubleshootNotificationsNode( val state = presenter.present() TroubleshootNotificationsView( state = state, - onBackClick = ::onDone, + onBackClick = callback::onDone, modifier = modifier, ) } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPoint.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPoint.kt index 0347d911e1..ca0cccc82c 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPoint.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPoint.kt @@ -9,7 +9,6 @@ package io.element.android.libraries.troubleshoot.impl.history import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node -import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.architecture.createNode @@ -17,18 +16,11 @@ import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint @ContributesBinding(AppScope::class) class DefaultPushHistoryEntryPoint : PushHistoryEntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): PushHistoryEntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : PushHistoryEntryPoint.NodeBuilder { - override fun callback(callback: PushHistoryEntryPoint.Callback): PushHistoryEntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: PushHistoryEntryPoint.Callback, + ): Node { + return parentNode.createNode(buildContext, listOf(callback)) } } diff --git a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt index 69070298ec..532c2230ee 100644 --- a/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt +++ b/libraries/troubleshoot/impl/src/main/kotlin/io/element/android/libraries/troubleshoot/impl/history/PushHistoryNode.kt @@ -12,11 +12,11 @@ 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 dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode +import io.element.android.libraries.architecture.callback 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 @@ -31,16 +31,10 @@ class PushHistoryNode( presenterFactory: PushHistoryPresenter.Factory, private val screenTracker: ScreenTracker, ) : Node(buildContext, plugins = plugins), PushHistoryNavigator { - private fun onDone() { - plugins().forEach { - it.onDone() - } - } + private val callback: PushHistoryEntryPoint.Callback = callback() override fun navigateTo(roomId: RoomId, eventId: EventId) { - plugins().forEach { - it.navigateTo(roomId, eventId) - } + callback.navigateToEvent(roomId, eventId) } private val presenter = presenterFactory.create(this) @@ -51,7 +45,7 @@ class PushHistoryNode( val state = presenter.present() PushHistoryView( state = state, - onBackClick = ::onDone, + onBackClick = callback::onDone, modifier = modifier, ) } diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPointTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPointTest.kt index e0d817a4ca..1eef24fbcb 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPointTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/DefaultNotificationTroubleShootEntryPointTest.kt @@ -34,11 +34,13 @@ class DefaultNotificationTroubleShootEntryPointTest { } val callback = object : NotificationTroubleShootEntryPoint.Callback { override fun onDone() = lambdaError() - override fun openIgnoredUsers() = lambdaError() + override fun navigateToBlockedUsers() = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(TroubleshootNotificationsNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt index f8be5a5696..751328578d 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/TroubleshootNotificationsPresenterTest.kt @@ -180,7 +180,7 @@ private fun createTroubleshootTestSuite( internal fun createTroubleshootNotificationsPresenter( navigator: NotificationTroubleshootNavigator = object : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = lambdaError() + override fun navigateToBlockedUsers() = lambdaError() }, troubleshootTestSuite: TroubleshootTestSuite = createTroubleshootTestSuite(), ): TroubleshootNotificationsPresenter { diff --git a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt index 858956488c..b963183ecc 100644 --- a/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt +++ b/libraries/troubleshoot/impl/src/test/kotlin/io/element/android/libraries/troubleshoot/impl/history/DefaultPushHistoryEntryPointTest.kt @@ -46,11 +46,13 @@ class DefaultPushHistoryEntryPointTest { } val callback = object : PushHistoryEntryPoint.Callback { override fun onDone() = lambdaError() - override fun navigateTo(roomId: RoomId, eventId: EventId) = lambdaError() + override fun navigateToEvent(roomId: RoomId, eventId: EventId) = lambdaError() } - val result = entryPoint.nodeBuilder(parentNode, BuildContext.root(null)) - .callback(callback) - .build() + val result = entryPoint.createNode( + parentNode = parentNode, + buildContext = BuildContext.root(null), + callback = callback, + ) assertThat(result).isInstanceOf(PushHistoryNode::class.java) assertThat(result.plugins).contains(callback) } diff --git a/libraries/troubleshoot/test/build.gradle.kts b/libraries/troubleshoot/test/build.gradle.kts index 8321ea5b4f..643afe8272 100644 --- a/libraries/troubleshoot/test/build.gradle.kts +++ b/libraries/troubleshoot/test/build.gradle.kts @@ -13,6 +13,7 @@ android { } dependencies { + implementation(projects.libraries.architecture) implementation(projects.libraries.troubleshoot.api) implementation(projects.tests.testutils) implementation(libs.coroutines.test) diff --git a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleShootEntryPoint.kt b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleShootEntryPoint.kt new file mode 100644 index 0000000000..38ba5bc32f --- /dev/null +++ b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleShootEntryPoint.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.troubleshoot.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.troubleshoot.api.NotificationTroubleShootEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakeNotificationTroubleShootEntryPoint : NotificationTroubleShootEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: NotificationTroubleShootEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt index 63445e5a3e..ea736b6a7b 100644 --- a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt +++ b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakeNotificationTroubleshootNavigator.kt @@ -13,5 +13,5 @@ import io.element.android.tests.testutils.lambda.lambdaError class FakeNotificationTroubleshootNavigator( private val openIgnoredUsersResult: () -> Unit = { lambdaError() }, ) : NotificationTroubleshootNavigator { - override fun openIgnoredUsers() = openIgnoredUsersResult() + override fun navigateToBlockedUsers() = openIgnoredUsersResult() } diff --git a/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakePushHistoryEntryPoint.kt b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakePushHistoryEntryPoint.kt new file mode 100644 index 0000000000..c87fe43575 --- /dev/null +++ b/libraries/troubleshoot/test/src/main/kotlin/io/element/android/libraries/troubleshoot/test/FakePushHistoryEntryPoint.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.troubleshoot.test + +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import io.element.android.libraries.troubleshoot.api.PushHistoryEntryPoint +import io.element.android.tests.testutils.lambda.lambdaError + +class FakePushHistoryEntryPoint : PushHistoryEntryPoint { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: PushHistoryEntryPoint.Callback, + ): Node = lambdaError() +} diff --git a/libraries/ui-strings/src/main/res/values-bg/translations.xml b/libraries/ui-strings/src/main/res/values-bg/translations.xml index 57b3a43d1a..42d2608ab6 100644 --- a/libraries/ui-strings/src/main/res/values-bg/translations.xml +++ b/libraries/ui-strings/src/main/res/values-bg/translations.xml @@ -316,6 +316,8 @@ "Отваряне в Google Maps" "Отваряне в OpenStreetMap" "Споделяне на това местоположение" + "Напускане на пространството" + "Защита и поверителност" "Местоположение" "Версия: %1$s (%2$s)" "bg" 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 8e86f59c3b..ae50c0f28d 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -97,6 +97,7 @@ "Zapomněli jste heslo?" "Přeposlat" "Přejít zpět" + "Přejít na role a oprávnění" "Přejít do nastavení" "Ignorovat" "Pozvat" @@ -178,7 +179,6 @@ "Pokročilá nastavení" "obrázek" "Analytika" - "Načítání oznámení…" "Opustili jste místnost" "Byli jste odhlášeni z relace" "Vzhled" @@ -476,6 +476,9 @@ Opravdu chcete pokračovat?" "%1$s prostor" "Prostory" "Zobrazit členy" + "Opustit prostor" + "Role a oprávnění" + "Zabezpečení a soukromí" "Zpráva nebyla odeslána, protože ověřená identita uživatele %1$s se změnila." "Zpráva nebyla odeslána, protože%1$s neověřil(a) všechna zařízení." "Zpráva nebyla odeslána, protože jste neověřili jedno nebo více zařízení." diff --git a/libraries/ui-strings/src/main/res/values-cy/translations.xml b/libraries/ui-strings/src/main/res/values-cy/translations.xml index 11b46cb85c..0253c6b01a 100644 --- a/libraries/ui-strings/src/main/res/values-cy/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cy/translations.xml @@ -497,6 +497,8 @@ Ydych chi\'n siŵr eich bod am barhau?" "%1$s • %2$s" "Gofod %1$s" "Gofodau" + "Gadael y gofod" + "Diogelwch a phreifatrwydd" "Heb anfon y neges oherwydd bod hunaniaeth wedi \'i ddilysu %1$s wedi\'i ailosod." "Heb anfon y neges oherwydd nid yw %1$s wedi gwirio pob dyfais." "Heb anfon y neges oherwydd nad ydych wedi gwirio un neu fwy o\'ch dyfeisiau." diff --git a/libraries/ui-strings/src/main/res/values-da/translations.xml b/libraries/ui-strings/src/main/res/values-da/translations.xml index fa91e60b9d..dd40b2e7bb 100644 --- a/libraries/ui-strings/src/main/res/values-da/translations.xml +++ b/libraries/ui-strings/src/main/res/values-da/translations.xml @@ -466,6 +466,8 @@ Er du sikker på, at du vil fortsætte?" "%1$s gruppe" "Grupper" "Vis medlemmer" + "Forlad gruppe" + "Sikkerhed og privatliv" "Beskeden blev ikke sendt fordi %1$s s bekræftede identitet blev nulstillet." "Meddelelsen er ikke sendt, fordi %1$s ikke har bekræftet alle enheder." "Beskeden er ikke sendt, fordi du ikke har verificeret en eller flere af dine enheder." 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 d1c8b7a6f4..3f34269879 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -420,6 +420,7 @@ Möchtest du wirklich fortfahren?" "🔐️ Begleite mich auf %1$s" "Hey, sprich mit mir auf %1$s: %2$s" "%1$s Android" + "Thread in %1$s" "Heftiges Schütteln um Fehler zu melden" "Bildschirmfoto" "%1$s: %2$s" @@ -466,6 +467,9 @@ Möchtest du wirklich fortfahren?" "%1$s • %2$s" "%1$s Space" "Spaces" + "Mitglieder anzeigen" + "Space verlassen" + "Sicherheit & Datenschutz" "Nachricht nicht gesendet, weil sich die verifizierte Identität von %1$s geändert hat." "Die Nachricht wurde nicht gesendet, weil %1$s nicht alle Geräte verifiziert hat." "Die Nachricht wurde nicht gesendet, weil du eines oder mehrere deiner Geräte nicht verifiziert hast." diff --git a/libraries/ui-strings/src/main/res/values-el/translations.xml b/libraries/ui-strings/src/main/res/values-el/translations.xml index 5085f404a1..bfb50b933e 100644 --- a/libraries/ui-strings/src/main/res/values-el/translations.xml +++ b/libraries/ui-strings/src/main/res/values-el/translations.xml @@ -394,6 +394,7 @@ "Άνοιγμα στο Google Maps" "Άνοιγμα στο OpenStreetMap" "Κοινή χρήση αυτής της τοποθεσίας" + "Ασφάλεια & απόρρητο" "Το μήνυμα δεν στάλθηκε γιατί έγινε επαναφορά της επαληθευμένης ταυτότητας του χρήστη %1$s." "Το μήνυμα δεν στάλθηκε επειδή ο χρήστης %1$s δεν έχει επαληθεύσει όλες τις συσκευές." "Το μήνυμα δεν στάλθηκε επειδή δεν έχεις επαληθεύσει τουλάχιστον μία από τις συσκευές σου." 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 4428e0b864..d4420e1f6b 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -377,6 +377,7 @@ Motivo: %1$s." "Abrir en Google Maps" "Abrir en OpenStreetMap" "Compartir esta ubicación" + "Seguridad y privacidad" "Mensaje no enviado porque la identidad verificada de %1$s fue restablecida." "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." 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 5f905c136d..e1e76f70da 100644 --- a/libraries/ui-strings/src/main/res/values-et/translations.xml +++ b/libraries/ui-strings/src/main/res/values-et/translations.xml @@ -95,6 +95,7 @@ "Kas unustasid salasõna?" "Edasta" "Tagasi eelmisesse vaatesse" + "Ava „Rollid ja õigused“" "Ava seadistused" "Eira" "Kutsu" @@ -420,6 +421,7 @@ Kas sa oled kindel, et soovid jätkata?" "🔐️ Liitu minuga rakenduses %1$s" "Hei, suhtle minuga %1$s võrgus: %2$s" "%1$s Android" + "Jutulõng „%1$s“ jututoas" "Veast teatamiseks raputa nutiseadet ägedalt" "Ekraanitõmmis" "%1$s: %2$s" @@ -467,6 +469,9 @@ Kas sa oled kindel, et soovid jätkata?" "Kogukond: %1$s" "Kogukonnad" "Vaata liikmeid" + "Lahku kogukonnast" + "Rollid ja õigused" + "Turvalisus ja privaatsus" "Sõnum on saatmata, kuna kasutaja %1$s verifitseeritud identiteet on lähtestatud." "Sõnum on saatmata, kuna %1$s pole verifitseerinud kõiki oma seadmeid." "Kuna sa pole üks või enamgi oma seadet verifitseerinud, siis sinu sõnum on saatmata." diff --git a/libraries/ui-strings/src/main/res/values-eu/translations.xml b/libraries/ui-strings/src/main/res/values-eu/translations.xml index 15bfa21fb5..394ee138a4 100644 --- a/libraries/ui-strings/src/main/res/values-eu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-eu/translations.xml @@ -375,6 +375,7 @@ Ziur jarraitu nahi duzula?" "Ireki Google Maps-en" "Ireki OpenStreetMap-en" "Partekatu kokapen hau" + "Segurtasuna eta pribatutasuna" "Kokapena" "Bertsioa: %1$s (%2$s)" "eu" diff --git a/libraries/ui-strings/src/main/res/values-fa/translations.xml b/libraries/ui-strings/src/main/res/values-fa/translations.xml index 6f55ce0db4..3842880477 100644 --- a/libraries/ui-strings/src/main/res/values-fa/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fa/translations.xml @@ -383,6 +383,8 @@ "‏%1$s فضا" "فضاها" "دیدن اعضا" + "ترک فضا" + "امنیت و محرمانگی" "مکان" "نگارش : %1$s (%2$s)" "fa" 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 1885f56f66..ae1e7bf433 100644 --- a/libraries/ui-strings/src/main/res/values-fi/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fi/translations.xml @@ -467,6 +467,8 @@ Haluatko varmasti jatkaa?" "%1$s tila" "Tilat" "Näytä jäsenet" + "Poistu tilasta" + "Turvallisuus ja yksityisyys" "Viestiä ei lähetetty, koska käyttäjän %1$s vahvistettu identiteetti nollattiin." "Viestiä ei lähetetty, koska %1$s ei ole vahvistanut kaikkia laitteitaan." "Viestiä ei lähetetty, koska et ole vahvistanut yhtä tai useampaa laitettasi." 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 72e29691fa..d222ffea88 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -467,6 +467,8 @@ Raison : %1$s." "Espace %1$s" "Espaces" "Voir les membres" + "Quitter l’espace" + "Sécurité & confidentialité" "Le message n’a pas été envoyé car l’identité vérifiée de %1$s a été réinitialisée." "Le message n’a pas été envoyé car %1$s n’a pas vérifié tous ses appareils." "Message non envoyé car vous n’avez pas vérifié tous vos appareils." 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 a55b85b944..44952a9151 100644 --- a/libraries/ui-strings/src/main/res/values-hu/translations.xml +++ b/libraries/ui-strings/src/main/res/values-hu/translations.xml @@ -465,6 +465,8 @@ Biztos, hogy folytatja?" "%1$s • %2$s" "%1$s tér" "Terek" + "Tér elhagyása" + "Biztonság és adatvédelem" "Az üzenet nem lett elküldve, mert %1$s ellenőrzött személyazonossága megváltozott." "Az üzenet nem lett elküldve, mert %1$s nem ellenőrizte az összes eszközét." "Az üzenet nem lett elküldve, mert egy vagy több eszközét nem ellenőrizte." diff --git a/libraries/ui-strings/src/main/res/values-in/translations.xml b/libraries/ui-strings/src/main/res/values-in/translations.xml index bd7739d5ba..d95548ce4c 100644 --- a/libraries/ui-strings/src/main/res/values-in/translations.xml +++ b/libraries/ui-strings/src/main/res/values-in/translations.xml @@ -399,6 +399,7 @@ Apakah Anda yakin ingin melanjutkan?" "Buka di Google Maps" "Buka di OpenStreetMap" "Bagikan lokasi ini" + "Keamanan & privasi" "Pesan tidak terkirim karena identitas terverifikasi %1$s telah diatur ulang." "Pesan tidak terkirim karena %1$s belum memverifikasi semua perangkat." "Pesan tidak terkirim karena Anda belum memverifikasi satu atau beberapa perangkat Anda." 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 77a1a20782..b0f637a133 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -447,6 +447,7 @@ Sei sicuro di voler continuare?" "Spazi che hai creato o a cui hai aderito." "%1$s • %2$s" "Spazi" + "Sicurezza e privacy" "Messaggio non inviato perché l\'identità verificata di %1$s è stata reimpostata." "Messaggio non inviato perché %1$s non ha verificato tutti i dispositivi." "Messaggio non inviato perché non hai verificato uno o più dispositivi." diff --git a/libraries/ui-strings/src/main/res/values-ko/translations.xml b/libraries/ui-strings/src/main/res/values-ko/translations.xml index 256e561db5..f7d3d65da5 100644 --- a/libraries/ui-strings/src/main/res/values-ko/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ko/translations.xml @@ -440,6 +440,7 @@ "당신이 스페이스를 만들거나 가입했습니다." "%1$s•%2$s" "스페이스" + "보안 및 개인정보 보호" "%1$s의 인증된 신원이 재설정되어 메시지가 전송되지 않았습니다." "%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 e64a005577..83694a16b8 100644 --- a/libraries/ui-strings/src/main/res/values-nb/translations.xml +++ b/libraries/ui-strings/src/main/res/values-nb/translations.xml @@ -465,6 +465,8 @@ Er du sikker på at du vil fortsette?" "%1$s område" "Områder" "Vis medlemmer" + "Forlat område" + "Sikkerhet og personvern" "Meldingen ble ikke sendt fordi %1$ss verifiserte identitet er tilbakestilt." "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." 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 e4186fd0f2..6ab2aa615d 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -475,6 +475,8 @@ Czy na pewno chcesz kontynuować?" "%1$s • %2$s" "Przestrzeń %1$s" "Przestrzenie" + "Opuść przestrzeń" + "Bezpieczeństwo i prywatność" "Wiadomość nie została wysłana, ponieważ tożsamość %1$s została zresetowana." "Wiadomość nie została wysłana, ponieważ %1$s nie zweryfikował wszystkich urządzeń." "Wiadomość nie została wysłana, ponieważ nie zweryfikowałeś jednego lub więcej swoich urządzeń." diff --git a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml index fabc600b67..8256d9b429 100644 --- a/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt-rBR/translations.xml @@ -428,6 +428,7 @@ Você tem certeza de que deseja continuar?" "Os espaços que você criou ou entrou." "%1$s • %2$s" "Espaços" + "Segurança e privacidade" "Mensagem não enviada porque a identidade verificada de %1$s foi redefinida." "A mensagem não foi enviada porque %1$s não verificou todos os dispositivos." "Mensagem não enviada porque você não verificou um ou mais dos seus dispositivos." 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 5e55f3a9de..b754842c1d 100644 --- a/libraries/ui-strings/src/main/res/values-pt/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pt/translations.xml @@ -461,6 +461,8 @@ Tens a certeza de que queres continuar?" "%1$s • %2$s" "Espaço %1$s" "Espaços" + "Sair do espaço" + "Segurança e privacidade" "Mensagem não enviada porque a identidade verificada de %1$s foi reposta." "Mensagem não enviada porque %1$s não verificou todos os dispositivos." "Mensagem não enviada porque não verificou um ou mais dos seus dispositivos." diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 0cf1acf673..e5936ee4bf 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -475,6 +475,8 @@ Sunteți sigur că doriți să continuați?" "%1$s • %2$s" "Spațiu %1$s" "Spații" + "Părăsiți spațiul" + "Securitate & confidențialitate" "Mesajul nu a fost trimis deoarece identitatea verificată a lui %1$s s-a schimbat." "Mesajul nu a fost trimis deoarece %1$s nu a verificat toate dispozitivele." "Mesajul nu a fost trimis deoarece nu ați verificat unul sau mai multe dispozitive." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index fc856adf94..6de1d6855c 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -473,6 +473,8 @@ "%1$s • %2$s" "%1$s пространство" "Пространства" + "Покинуть пространство" + "Безопасность и конфиденциальность" "Сообщение не отправлено, потому что подтвержденная личность %1$s была сброшена." "Сообщение не отправлено, потому что %1$s не проверил одно или несколько устройств." "Сообщение не отправлено, поскольку вы не подтвердили одно или несколько своих устройств." 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 263c1282b5..ed2fe96842 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -97,6 +97,7 @@ "Zabudnuté heslo?" "Preposlať" "Ísť späť" + "Prejsť na roly a oprávnenia" "Prejsť na nastavenia" "Ignorovať" "Pozvať" @@ -178,7 +179,6 @@ "Pokročilé nastavenia" "obrázok" "Analytika" - "Načítavajú sa upozornenia…" "Opustili ste miestnosť" "Boli ste odhlásení zo relácie." "Vzhľad" @@ -478,6 +478,9 @@ Naozaj chcete pokračovať?" "%1$s priestor" "Priestory" "Zobraziť členov" + "Opustiť priestor" + "Roly a oprávnenia" + "Bezpečnosť a súkromie" "Správa nebola odoslaná, pretože sa zmenila overená totožnosť používateľa %1$s." "Správa nebola odoslaná, pretože %1$s neoveril/a všetky zariadenia." "Správa nebola odoslaná, pretože ste neoverili jedno alebo viac svojich zariadení." 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 e3cf26193e..838f17e2ff 100644 --- a/libraries/ui-strings/src/main/res/values-sv/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sv/translations.xml @@ -445,6 +445,7 @@ Anledning:%1$s." "Utrymmen som du har skapat eller gått med i." "%1$s • %2$s" "Utrymmen" + "Säkerhet och sekretess" "Meddelandet skickades inte eftersom verifierad identitet för %1$s återställdes." "Meddelandet skickades inte eftersom %1$s inte har verifierat alla enheter." "Meddelandet skickades inte eftersom du inte har verifierat en eller flera av dina enheter." diff --git a/libraries/ui-strings/src/main/res/values-tr/translations.xml b/libraries/ui-strings/src/main/res/values-tr/translations.xml index c72f5a6139..b93df93242 100644 --- a/libraries/ui-strings/src/main/res/values-tr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-tr/translations.xml @@ -365,6 +365,7 @@ Devam etmek istediğinizden emin misiniz?" "Google Maps\'te aç" "OpenStreetMap\'te aç" "Bu konumu paylaş" + "Güvenlik ve gizlilik" "%1$s kullanıcısının doğrulanmış kimliği değiştiği için ileti gönderilmedi." "%1$s tüm cihazları doğrulamadığı için mesaj gönderilmedi." "Bir veya daha fazla cihazınızı doğrulamadığınız için mesaj gönderilmedi." 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 a4c171a21b..e67544455a 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -461,6 +461,7 @@ "Простори, які ви створили або до яких приєдналися." "%1$s • %2$s" "Простори" + "Безпека й приватність" "Повідомлення не надіслано, оскільки підтверджену особистість %1$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 3f6aa4f320..9bfee19c57 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 @@ -412,6 +412,7 @@ "🔐️ 在 %1$s 上加入我" "嘿,來 %1$s 和我聊天:%2$s" "%1$s Android" + "在 %1$s 的討論串" "憤怒搖晃以回報臭蟲" "螢幕截圖" "%1$s:%2$s" @@ -457,6 +458,9 @@ "%1$s • %2$s" "%1$s 空間" "空間" + "檢視成員" + "離開空間" + "安全與隱私" "因為 %1$s 的驗證身份已重設,因此未傳送訊息。" "訊息未傳送,因為 %1$s 尚未驗證所有裝置。" "因為您尚未驗證一個或多個裝置,因此未傳送訊息" diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index a4144eb8f0..955df716b8 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -452,6 +452,8 @@ "您创建或加入的空间。" "%1$s • %2$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 8ca9198cfe..47fd764f6c 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -95,6 +95,7 @@ "Forgot password?" "Forward" "Go back" + "Go to roles & permissions" "Go to settings" "Ignore" "Invite" @@ -176,7 +177,6 @@ "Advanced settings" "an image" "Analytics" - "Fetching notifications…" "You left the room" "You were logged out of the session" "Appearance" @@ -469,6 +469,9 @@ Are you sure you want to continue?" "%1$s space" "Spaces" "View members" + "Leave space" + "Roles & permissions" + "Security & privacy" "Message not sent because %1$s’s verified identity was reset." "Message not sent because %1$s has not verified all devices." "Message not sent because you have not verified one or more of your devices." diff --git a/libraries/ui-utils/build.gradle.kts b/libraries/ui-utils/build.gradle.kts index 95ce3d21a1..227bdd7f49 100644 --- a/libraries/ui-utils/build.gradle.kts +++ b/libraries/ui-utils/build.gradle.kts @@ -13,11 +13,11 @@ plugins { android { namespace = "io.element.android.libraries.ui.utils" - - dependencies { - implementation(projects.libraries.androidutils) - implementation(projects.services.toolbox.impl) - - testCommonDependencies(libs) - } +} + +dependencies { + implementation(projects.libraries.androidutils) + implementation(projects.services.toolbox.impl) + + testCommonDependencies(libs) } diff --git a/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessagePresenter.kt b/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessagePresenter.kt index c7811f6d17..74c6aae39a 100644 --- a/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessagePresenter.kt +++ b/libraries/voiceplayer/impl/src/main/kotlin/io/element/android/libraries/voiceplayer/impl/VoiceMessagePresenter.kt @@ -84,7 +84,7 @@ class VoiceMessagePresenter( } } - fun eventSink(event: VoiceMessageEvents) { + fun handleEvent(event: VoiceMessageEvents) { when (event) { is VoiceMessageEvents.PlayPause -> { if (playerState.isPlaying) { @@ -119,7 +119,7 @@ class VoiceMessagePresenter( progress = progress, time = time, showCursor = showCursor, - eventSink = { eventSink(it) }, + eventSink = ::handleEvent, ) } } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt index 1c5570db94..2a986fcc28 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/SessionWellknownRetriever.kt @@ -8,6 +8,5 @@ package io.element.android.libraries.wellknown.api interface SessionWellknownRetriever { - suspend fun getWellKnown(): WellknownRetrieverResult suspend fun getElementWellKnown(): WellknownRetrieverResult } diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellKnown.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellKnown.kt deleted file mode 100644 index 59f63d1655..0000000000 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellKnown.kt +++ /dev/null @@ -1,17 +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.wellknown.api - -data class WellKnown( - val homeServer: WellKnownBaseConfig?, - val identityServer: WellKnownBaseConfig?, -) - -data class WellKnownBaseConfig( - val baseURL: String? -) diff --git a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt index 4675a0cb18..5c146fbbad 100644 --- a/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt +++ b/libraries/wellknown/api/src/main/kotlin/io/element/android/libraries/wellknown/api/WellknownRetriever.kt @@ -8,6 +8,5 @@ package io.element.android.libraries.wellknown.api interface WellknownRetriever { - suspend fun getWellKnown(baseUrl: String): WellknownRetrieverResult suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult } diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt index ad463bd2d2..a4b394a427 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetriever.kt @@ -15,7 +15,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.SessionWellknownRetriever -import io.element.android.libraries.wellknown.api.WellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import timber.log.Timber @@ -26,17 +25,6 @@ class DefaultSessionWellknownRetriever( ) : SessionWellknownRetriever { private val domain by lazy { matrixClient.userIdServerName() } - override suspend fun getWellKnown(): WellknownRetrieverResult { - val url = "https://$domain/.well-known/matrix/client" - return matrixClient - .getUrl(url) - .mapCatchingExceptions { - val data = String(it) - json().decodeFromString(data).map() - } - .toWellknownRetrieverResult() - } - override suspend fun getElementWellKnown(): WellknownRetrieverResult { val url = "https://$domain/.well-known/element/element.json" return matrixClient diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt index a6014f773c..1312436654 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/DefaultWellknownRetriever.kt @@ -13,7 +13,6 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.network.RetrofitFactory import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.WellKnown import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import retrofit2.HttpException @@ -24,27 +23,6 @@ import java.net.HttpURLConnection class DefaultWellknownRetriever( private val retrofitFactory: RetrofitFactory, ) : WellknownRetriever { - override suspend fun getWellKnown(baseUrl: String): WellknownRetrieverResult { - return buildWellknownApi(baseUrl) - .map { wellknownApi -> - try { - val result = wellknownApi.getWellKnown().map() - WellknownRetrieverResult.Success(result) - } catch (e: Exception) { - Timber.e(e, "Failed to retrieve well-known data for $baseUrl") - if ((e as? HttpException)?.code() == HttpURLConnection.HTTP_NOT_FOUND) { - WellknownRetrieverResult.NotFound - } else { - WellknownRetrieverResult.Error(e) - } - } - } - .fold( - onSuccess = { it }, - onFailure = { WellknownRetrieverResult.Error(it as Exception) } - ) - } - override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult { return buildWellknownApi(baseUrl) .map { wellknownApi -> diff --git a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt index 169757caa9..3b705e09c8 100644 --- a/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt +++ b/libraries/wellknown/impl/src/main/kotlin/io/element/android/libraries/wellknown/impl/Mapper.kt @@ -8,8 +8,6 @@ package io.element.android.libraries.wellknown.impl import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.WellKnown -import io.element.android.libraries.wellknown.api.WellKnownBaseConfig internal fun InternalElementWellKnown.map() = ElementWellKnown( registrationHelperUrl = registrationHelperUrl, @@ -17,12 +15,3 @@ internal fun InternalElementWellKnown.map() = ElementWellKnown( rageshakeUrl = rageshakeUrl, brandColor = brandColor, ) - -internal fun InternalWellKnown.map() = WellKnown( - homeServer = homeServer?.map(), - identityServer = identityServer?.map(), -) - -internal fun InternalWellKnownBaseConfig.map() = WellKnownBaseConfig( - baseURL = baseURL, -) diff --git a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt index 12b961ff9e..63dca38aac 100644 --- a/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt +++ b/libraries/wellknown/impl/src/test/kotlin/io/element/android/libraries/wellknown/impl/DefaultSessionWellknownRetrieverTest.kt @@ -12,8 +12,6 @@ import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.WellKnown -import io.element.android.libraries.wellknown.api.WellKnownBaseConfig import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -21,142 +19,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test class DefaultSessionWellknownRetrieverTest { - @Test - fun `get empty wellknown`() = runTest { - val getUrlLambda = lambdaRecorder> { - Result.success("{}".toByteArray()) - } - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = getUrlLambda, - ) - assertThat(sut.getWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - WellKnown( - homeServer = null, - identityServer = null, - ) - ) - ) - getUrlLambda.assertions().isCalledOnce() - .with(value("https://user.domain.org/.well-known/matrix/client")) - } - - @Test - fun `get wellknown with full content`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "m.homeserver": { - "base_url": "https://example.org" - }, - "m.identity_server": { - "base_url": "https://identity.example.org" - } - }""".trimIndent().toByteArray() - ) - } - ) - assertThat(sut.getWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - WellKnown( - homeServer = WellKnownBaseConfig( - baseURL = "https://example.org", - ), - identityServer = WellKnownBaseConfig( - baseURL = "https://identity.example.org", - ), - ) - ) - ) - } - - @Test - fun `get wellknown with full content empty base_url`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "m.homeserver": { - "base_url": "https://example.org" - }, - "m.identity_server": {} - }""".trimIndent().toByteArray() - ) - } - ) - assertThat(sut.getWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - WellKnown( - homeServer = WellKnownBaseConfig( - baseURL = "https://example.org", - ), - identityServer = WellKnownBaseConfig( - baseURL = null, - ), - ) - ) - ) - } - - @Test - fun `get wellknown with unknown key`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "m.homeserver": { - "base_url": "https://example.org" - }, - "m.identity_server": { - "base_url": "https://identity.example.org" - }, - "other": true - }""".trimIndent().toByteArray() - ) - }, - ) - assertThat(sut.getWellKnown()).isEqualTo( - WellknownRetrieverResult.Success( - WellKnown( - homeServer = WellKnownBaseConfig( - baseURL = "https://example.org", - ), - identityServer = WellKnownBaseConfig( - baseURL = "https://identity.example.org", - ), - ) - ) - ) - } - - @Test - fun `get wellknown json error`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.success( - """{ - "m.homeserver": { - "base_url": "https://example.org" - }, - error - }""".trimIndent().toByteArray() - ) - } - ) - assertThat(sut.getWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - - @Test - fun `get wellknown network error`() = runTest { - val sut = createDefaultSessionWellknownRetriever( - getUrlLambda = { - Result.failure(AN_EXCEPTION) - } - ) - assertThat(sut.getWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) - } - @Test fun `get empty element wellknown`() = runTest { val getUrlLambda = lambdaRecorder> { diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt index d90a02ac96..5ab66701be 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeSessionWellknownRetriever.kt @@ -9,18 +9,12 @@ package io.element.android.features.wellknown.test import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.SessionWellknownRetriever -import io.element.android.libraries.wellknown.api.WellKnown import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.simulateLongTask class FakeSessionWellknownRetriever( - private val getWellKnownResult: () -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, private val getElementWellKnownResult: () -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, ) : SessionWellknownRetriever { - override suspend fun getWellKnown(): WellknownRetrieverResult = simulateLongTask { - getWellKnownResult() - } - override suspend fun getElementWellKnown(): WellknownRetrieverResult = simulateLongTask { getElementWellKnownResult() } diff --git a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt index 52bb1cfa4c..4c4a31a572 100644 --- a/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt +++ b/libraries/wellknown/test/src/main/kotlin/io/element/android/features/wellknown/test/FakeWellknownRetriever.kt @@ -8,19 +8,13 @@ package io.element.android.features.wellknown.test import io.element.android.libraries.wellknown.api.ElementWellKnown -import io.element.android.libraries.wellknown.api.WellKnown import io.element.android.libraries.wellknown.api.WellknownRetriever import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.tests.testutils.simulateLongTask class FakeWellknownRetriever( - private val getWellKnownResult: (String) -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, private val getElementWellKnownResult: (String) -> WellknownRetrieverResult = { WellknownRetrieverResult.NotFound }, ) : WellknownRetriever { - override suspend fun getWellKnown(baseUrl: String): WellknownRetrieverResult = simulateLongTask { - getWellKnownResult(baseUrl) - } - override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult = simulateLongTask { getElementWellKnownResult(baseUrl) } diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 673f3cb724..819554ae55 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -38,7 +38,7 @@ private const val versionYear = 25 * Month of the version on 2 digits. Value must be in [1,12]. * Do not update this value. it is updated by the release script. */ -private const val versionMonth = 10 +private const val versionMonth = 11 /** * Release number in the month. Value must be in [0,99]. diff --git a/screenshots/de/appnav.room.joined_LoadingRoomNodeView_Day_1_de.png b/screenshots/de/appnav.room.joined_LoadingRoomNodeView_Day_1_de.png index 04fcabdf4d..f7a5852846 100644 --- a/screenshots/de/appnav.room.joined_LoadingRoomNodeView_Day_1_de.png +++ b/screenshots/de/appnav.room.joined_LoadingRoomNodeView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf9acfcae8fa5c62a6d002ea931165e98b0b9389714f2b5a012d59fc607be388 -size 11051 +oid sha256:173c488c77b3ba1f0b8190c626e51d12d221e4b85aaa83b3fe5caf3e25fe458d +size 10189 diff --git a/screenshots/de/features.messages.impl.forward_ForwardMessagesView_Day_3_de.png b/screenshots/de/features.forward.impl_ForwardMessagesView_Day_3_de.png similarity index 100% rename from screenshots/de/features.messages.impl.forward_ForwardMessagesView_Day_3_de.png rename to screenshots/de/features.forward.impl_ForwardMessagesView_Day_3_de.png diff --git a/screenshots/de/features.roomdetails.impl.edit_RoomDetailsEditView_Day_8_de.png b/screenshots/de/features.roomdetails.impl.edit_RoomDetailsEditView_Day_8_de.png new file mode 100644 index 0000000000..02051a08da --- /dev/null +++ b/screenshots/de/features.roomdetails.impl.edit_RoomDetailsEditView_Day_8_de.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12f8ce1dbeb1a16ab8351486310fdeabba566db4aad460fc4e1e1f7fdbe63bf7 +size 37209 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 482c1c948f..8e26af29d4 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:fc00250676d4b227efd4792461f78ea76b4fb7be4200b5fddb7419679a385181 -size 47183 +oid sha256:8b5883dce06f6c481b1d20e08ba4833f67b69714c23c60b4ffe88d0dee0d4797 +size 52280 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png index 423f728994..66bcb88c50 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bd48a463398ab53d8930c31ed8351b597b98b8502a8f5b1d634b7a802b13224 -size 34842 +oid sha256:6efe4f355c6372ad031136f6f2de51c3c6132e18ddc70f558c16ee921befefa0 +size 34819 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png index 238139c94f..71f98c9def 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_1_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:01b313b5135b1c2dbc4bd808472c9d3113f4e1aba4ddea8111bae9038f7a5bfc -size 35886 +oid sha256:3af3d6c1c57c37f88f37624614a1bca7e9fcae0042482d293f2eef69a7350713 +size 35864 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png index ac2147d97f..68e0fdf009 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_2_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1f22f80f55dfbcc863e9ae1770aa5de2ea1c80f389fca3b4460b51856144cac -size 36184 +oid sha256:65fab252bd1c0920692404f0067323bfb33df9124ba5dc3ad5be5008295fbc0f +size 36158 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png index e79a267816..1c22e938cb 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_3_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6b4b1feee6815dab72dc3c48e082dcde654e7c9c611a5ecc8ebe5cbfbbc1ebb -size 65211 +oid sha256:ecb9ad959737f80864480f7fa3cce8e31a1b0c2aa84d5b49ff8b119b1aa5bc1e +size 65190 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png index 45495330f9..52dd6c93a0 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_4_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4fd28760ff4386ea8fbc45ab5d42a5d89881afbd4771c978d45e0a279bb75fc -size 65828 +oid sha256:fcdf0489810fe2035f072cdb5cc5e3c78cb3ee4e5d220148ed2721dbf324d7e6 +size 65805 diff --git a/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png b/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png index f68d2fba50..ff45615538 100644 --- a/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png +++ b/screenshots/de/features.space.impl.root_SpaceView_Day_5_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:601299d37927f03b696a2589ebcb481ae7efa13a35c49f9621f786a5f87f3130 -size 60275 +oid sha256:7a9166782d34d5388b8cdcdf270f8ac854bad44e24a30f35661af2a93cdbeb4a +size 60269 diff --git a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png index 8be502c48e..eebead276c 100644 --- a/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57800bd0df9e4eabd4ed60d9868ba4e0dd82ea2268567893252b21b3bdf769e2 -size 43078 +oid sha256:bea0bbfeeffc6e4fcfcf5c2802e415eec785f923f114afcb31f8e80f49b97f01 +size 45514 diff --git a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png index 91c9af4023..90ed906e03 100644 --- a/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:748a0d0bd122a55520ebdd673300321fb41c06b24f84baeeab17c4a253e34cc2 -size 45804 +oid sha256:be243fc9a55bd214187605b79226885382f0a92601ceeac4e6495179d982683d +size 45443 diff --git a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png index bf12cfa991..212b565f9f 100644 --- a/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png +++ b/screenshots/de/libraries.mediaviewer.impl.viewer_MediaViewerView_11_de.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19e9bd753d0e325d905ec71b9f4912eb4f10264ea9a92a98346b209ec039c856 -size 42635 +oid sha256:1b8297bd3c5532a327728b0d517e6a634bf4f689df3f3e8a039814eb26f659af +size 43984 diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 650364a46c..317e8e04d7 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,80 +1,80 @@ // 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",20385,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20392,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20385,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20385,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20385,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20385,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20385,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20385,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20385,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20385,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20385,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20385,], -["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20385,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20392,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20392,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20392,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20392,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20392,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20392,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20392,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20392,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20392,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20392,], +["features.login.impl.accountprovider_AccountProviderOtherView_Day_0_en","features.login.impl.accountprovider_AccountProviderOtherView_Night_0_en",20392,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_1_en","features.login.impl.accountprovider_AccountProviderView_Night_1_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_2_en","features.login.impl.accountprovider_AccountProviderView_Night_2_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_3_en","features.login.impl.accountprovider_AccountProviderView_Night_3_en",0,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20385,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20385,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20392,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20392,], ["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",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20385,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20392,], ["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",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20385,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20385,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20385,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20385,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20385,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20385,], -["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20385,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20385,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20385,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20392,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20392,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20392,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20392,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20392,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_0_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_1_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_2_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_3_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_4_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_5_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_6_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_7_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewDark_8_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_0_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_1_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_2_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_3_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_4_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_5_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_6_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_7_en","",20392,], +["features.preferences.impl.advanced_AdvancedSettingsViewLight_8_en","",20392,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20392,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20392,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20385,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20385,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20385,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20385,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20385,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20392,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20392,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20392,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20392,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20392,], ["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",20385,], +["services.apperror.impl_AppErrorView_Day_0_en","services.apperror.impl_AppErrorView_Night_0_en",20392,], ["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",20385,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20392,], ["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",20385,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20392,], ["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",20385,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20392,], ["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",20385,], +["features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Day_0_en","features.messages.impl.messagecomposer_AttachmentSourcePickerMenu_Night_0_en",20392,], ["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,], @@ -84,19 +84,19 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_6_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20385,], -["features.messages.impl.attachments.preview_AttachmentsView_8_en","",20385,], +["features.messages.impl.attachments.preview_AttachmentsView_0_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_1_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_2_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_3_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_4_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_5_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_6_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_7_en","",20392,], +["features.messages.impl.attachments.preview_AttachmentsView_8_en","",20392,], ["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",20385,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20392,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Day_0_en","libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Night_0_en",0,], ["libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Day_1_en","libraries.designsystem.components.avatar_AvatarRowLastOnTopRtl_Night_1_en",0,], @@ -123,22 +123,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20385,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20392,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20385,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20385,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20392,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20392,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20385,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20385,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20385,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20385,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20385,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20392,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20392,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20392,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20392,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20392,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -148,125 +148,125 @@ export const screenshots = [ ["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",20385,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20385,], +["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",20392,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20392,], ["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20385,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20385,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20385,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20385,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20385,], -["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_0_en","features.changeroommemberroles.impl_ChangeRolesView_Night_0_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_10_en","features.changeroommemberroles.impl_ChangeRolesView_Night_10_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_11_en","features.changeroommemberroles.impl_ChangeRolesView_Night_11_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_12_en","features.changeroommemberroles.impl_ChangeRolesView_Night_12_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_1_en","features.changeroommemberroles.impl_ChangeRolesView_Night_1_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_2_en","features.changeroommemberroles.impl_ChangeRolesView_Night_2_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_3_en","features.changeroommemberroles.impl_ChangeRolesView_Night_3_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_4_en","features.changeroommemberroles.impl_ChangeRolesView_Night_4_en",20385,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20392,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20392,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20392,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20392,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_0_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_0_en",20392,], +["features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Day_1_en","features.login.impl.screens.changeaccountprovider_ChangeAccountProviderView_Night_1_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_0_en","features.changeroommemberroles.impl_ChangeRolesView_Night_0_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_10_en","features.changeroommemberroles.impl_ChangeRolesView_Night_10_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_11_en","features.changeroommemberroles.impl_ChangeRolesView_Night_11_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_12_en","features.changeroommemberroles.impl_ChangeRolesView_Night_12_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_1_en","features.changeroommemberroles.impl_ChangeRolesView_Night_1_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_2_en","features.changeroommemberroles.impl_ChangeRolesView_Night_2_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_3_en","features.changeroommemberroles.impl_ChangeRolesView_Night_3_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_4_en","features.changeroommemberroles.impl_ChangeRolesView_Night_4_en",20392,], ["features.changeroommemberroles.impl_ChangeRolesView_Day_5_en","features.changeroommemberroles.impl_ChangeRolesView_Night_5_en",0,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_6_en","features.changeroommemberroles.impl_ChangeRolesView_Night_6_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_7_en","features.changeroommemberroles.impl_ChangeRolesView_Night_7_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_8_en","features.changeroommemberroles.impl_ChangeRolesView_Night_8_en",20385,], -["features.changeroommemberroles.impl_ChangeRolesView_Day_9_en","features.changeroommemberroles.impl_ChangeRolesView_Night_9_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20385,], -["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20385,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_6_en","features.changeroommemberroles.impl_ChangeRolesView_Night_6_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_7_en","features.changeroommemberroles.impl_ChangeRolesView_Night_7_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_8_en","features.changeroommemberroles.impl_ChangeRolesView_Night_8_en",20392,], +["features.changeroommemberroles.impl_ChangeRolesView_Day_9_en","features.changeroommemberroles.impl_ChangeRolesView_Night_9_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_0_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_1_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_2_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_3_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_4_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_5_en",20392,], +["features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions.permissions_ChangeRoomPermissionsView_Night_6_en",20392,], ["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",20385,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20385,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20385,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20385,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20392,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20392,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20392,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20392,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20385,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20392,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20385,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20385,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20385,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20385,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20385,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20385,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20385,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20392,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20392,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20392,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20392,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20392,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20392,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20392,], ["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",20385,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20385,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20385,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20392,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20392,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20392,], ["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], ["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], ["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_en",0,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20385,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20385,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20385,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20385,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20385,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20385,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20392,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20392,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20392,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20392,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20392,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20392,], ["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_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_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",20385,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20385,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20385,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20385,], -["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20385,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20385,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20385,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20385,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20385,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20385,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20385,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20385,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20385,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20385,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20385,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20385,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20385,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20385,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20385,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20385,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20392,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_0_en","features.login.impl.screens.createaccount_CreateAccountView_Night_0_en",20392,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_1_en","features.login.impl.screens.createaccount_CreateAccountView_Night_1_en",20392,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_2_en","features.login.impl.screens.createaccount_CreateAccountView_Night_2_en",20392,], +["features.login.impl.screens.createaccount_CreateAccountView_Day_3_en","features.login.impl.screens.createaccount_CreateAccountView_Night_3_en",20392,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20392,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20392,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20392,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20392,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20392,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20392,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20392,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20392,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20392,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20392,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20392,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20392,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20392,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20392,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20392,], ["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","",20385,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20385,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20385,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20385,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20385,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20385,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20385,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20392,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20392,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20392,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20392,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20392,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20392,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20392,], ["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",20385,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20385,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20385,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20392,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20392,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20392,], ["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",20385,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20392,], ["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",20385,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20385,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20385,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20385,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20385,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20385,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20385,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_3_en","features.preferences.impl.developer_DeveloperSettingsView_Night_3_en",20385,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20392,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20392,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20392,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20392,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20392,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20392,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20392,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_3_en","features.preferences.impl.developer_DeveloperSettingsView_Night_3_en",20392,], ["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,], @@ -281,18 +281,18 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20385,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20385,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20385,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20385,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20385,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20385,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20385,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20385,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20385,], -["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20385,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20385,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20385,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20392,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20392,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20392,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20392,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20392,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_0_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_0_en",20392,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_1_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_1_en",20392,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_2_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_2_en",20392,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_3_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_3_en",20392,], +["features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Day_4_en","features.roomdetails.impl.securityandprivacy.editroomaddress_EditRoomAddressView_Night_4_en",20392,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20392,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20392,], ["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,], @@ -303,14 +303,14 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMediumNoBlurShadow_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["features.messages.impl.timeline.components.customreaction_EmojiItem_Day_0_en","features.messages.impl.timeline.components.customreaction_EmojiItem_Night_0_en",0,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20385,], -["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20385,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_0_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_0_en",20392,], +["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_1_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_1_en",20392,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_2_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_2_en",0,], ["features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Day_3_en","features.messages.impl.timeline.components.customreaction.picker_EmojiPicker_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20385,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20385,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20385,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20392,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20392,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20392,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], @@ -329,43 +329,43 @@ 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",20385,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20385,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20385,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20392,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20392,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20392,], ["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",20385,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20385,], +["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], +["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], +["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20395,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20392,], ["libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButtonCircleShape_Night_0_en",0,], ["libraries.designsystem.components.button_GradientFloatingActionButton_Day_0_en","libraries.designsystem.components.button_GradientFloatingActionButton_Night_0_en",0,], ["features.messages.impl.timeline.components.group_GroupHeaderView_Day_0_en","features.messages.impl.timeline.components.group_GroupHeaderView_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20385,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20385,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20388,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20388,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20388,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20392,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20392,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20392,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20392,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20392,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20385,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20385,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20392,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20392,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20385,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20385,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20385,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20385,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20385,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20385,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20385,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20385,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20385,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20385,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20385,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20385,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20392,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20392,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20392,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20392,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20392,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20392,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20392,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20392,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20392,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20392,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20392,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20392,], ["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,], @@ -378,8 +378,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_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",20385,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20385,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20392,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20392,], ["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,], @@ -387,109 +387,109 @@ 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",20385,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20392,], ["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",20385,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20392,], ["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",20385,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20392,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20385,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20385,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20392,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20392,], ["libraries.designsystem.atomic.molecules_InfoListItemMolecule_Day_0_en","libraries.designsystem.atomic.molecules_InfoListItemMolecule_Night_0_en",0,], ["libraries.designsystem.atomic.organisms_InfoListOrganism_Day_0_en","libraries.designsystem.atomic.organisms_InfoListOrganism_Night_0_en",0,], ["libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20385,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20385,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20385,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20392,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20392,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20392,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20385,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20385,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20385,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20385,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20392,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20392,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20392,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20392,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20385,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20385,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20385,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20392,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20392,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20392,], ["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",20385,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20385,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20385,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20385,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20385,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20392,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20392,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20392,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20392,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20385,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20385,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20392,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20392,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20385,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20385,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20385,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20392,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20392,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20392,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20385,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20392,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -544,37 +544,37 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], ["libraries.textcomposer.components_LiveWaveformView_Day_0_en","libraries.textcomposer.components_LiveWaveformView_Night_0_en",0,], ["appnav.room.joined_LoadingRoomNodeView_Day_0_en","appnav.room.joined_LoadingRoomNodeView_Night_0_en",0,], -["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20385,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20385,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20385,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20385,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20392,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20392,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20392,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20392,], ["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",20385,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20385,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20385,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20385,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20385,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20385,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20385,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20385,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20385,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20385,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20385,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20385,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20385,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20385,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20385,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20385,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20385,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20385,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20385,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20385,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20385,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20385,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20385,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20385,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20392,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20392,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20392,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20392,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20392,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20392,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20392,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20392,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20392,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20392,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20392,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20392,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20392,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20392,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20392,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20392,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20392,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20392,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20392,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20392,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20392,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20392,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20392,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20392,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20385,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20392,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -587,22 +587,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",20385,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20385,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20392,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20392,], ["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",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20385,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20385,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20392,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20392,], ["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,], @@ -610,14 +610,14 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20385,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20385,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20392,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20392,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20385,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20392,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_1_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20385,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20392,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], @@ -631,7 +631,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",20385,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20392,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -640,7 +640,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20385,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20392,], ["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,], @@ -648,137 +648,137 @@ 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.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20385,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20385,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20385,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20385,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20385,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20385,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20385,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20385,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20385,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20385,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20385,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20385,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20385,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20385,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20392,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20392,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20392,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20392,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20392,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20392,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20392,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20392,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20392,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20392,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20392,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20392,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20392,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20392,], ["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",20385,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20392,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], ["appicon.element_MonochromeIcon_en","",0,], -["features.preferences.impl.root_MultiAccountSection_Day_0_en","features.preferences.impl.root_MultiAccountSection_Night_0_en",20385,], +["features.preferences.impl.root_MultiAccountSection_Day_0_en","features.preferences.impl.root_MultiAccountSection_Night_0_en",20392,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_MultipleSelectionDialog_Night_0_en",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelectedTrailingContent_Multiple_selection_List_item_-_selection_in_trailing_content_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20385,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20385,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20385,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20392,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20392,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20392,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20385,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20385,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20392,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20392,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20385,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20392,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20385,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20385,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20392,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20392,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20385,], -["features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en","features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en",20385,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20385,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20385,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20385,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20385,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20392,], +["features.changeroommemberroles.impl_PendingMemberRowWithLongName_Day_0_en","features.changeroommemberroles.impl_PendingMemberRowWithLongName_Night_0_en",20392,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20392,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20392,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20392,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20392,], ["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",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20385,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20385,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20392,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20392,], ["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",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20385,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20385,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20385,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20385,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20385,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20385,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20392,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20392,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20392,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20392,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20392,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20392,], ["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",20385,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20385,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20385,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20385,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20385,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20392,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20392,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20392,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20392,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20392,], ["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",20385,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20385,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20385,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20385,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20385,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20385,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20385,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20385,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20385,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20385,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20385,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20392,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20392,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20392,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20392,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20392,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20392,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20392,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20392,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20392,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20392,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20392,], ["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,], @@ -792,208 +792,209 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20385,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20385,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20385,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20385,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20392,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20392,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20392,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20392,], ["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","",20385,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20385,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20392,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20392,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20385,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20385,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20385,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20385,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20385,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20385,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20385,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20385,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20385,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20385,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20385,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20385,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20385,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20385,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20385,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20385,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20392,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20392,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20392,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20392,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20392,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20392,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20392,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20392,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20392,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20392,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20392,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20392,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20392,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20392,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20392,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20392,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20385,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20385,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20392,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20385,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20385,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20385,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20385,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20385,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20385,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20392,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20392,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20392,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20392,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20392,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20392,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20392,], ["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",20385,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20385,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20385,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20385,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20385,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20385,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20385,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20385,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20385,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20385,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20385,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20385,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20385,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20385,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20385,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20385,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20385,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20392,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20392,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20392,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20392,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20392,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20392,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20392,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20392,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20392,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20392,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20392,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20392,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20392,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20392,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20392,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20392,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20392,], ["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",20385,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20385,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20385,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20385,], -["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en",20385,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20392,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20392,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20392,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_0_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_0_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_1_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_1_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_2_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_2_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_3_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_3_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_4_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_4_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_5_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_5_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_6_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_6_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_7_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_7_en",20392,], +["features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Day_8_en","features.roomdetails.impl.rolesandpermissions_RolesAndPermissionsView_Night_8_en",20392,], ["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",20385,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20385,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20385,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20385,], -["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20385,], -["features.roomdetails.impl_RoomDetails_0_en","",20385,], -["features.roomdetails.impl_RoomDetails_10_en","",20385,], -["features.roomdetails.impl_RoomDetails_11_en","",20385,], -["features.roomdetails.impl_RoomDetails_12_en","",20385,], -["features.roomdetails.impl_RoomDetails_13_en","",20385,], -["features.roomdetails.impl_RoomDetails_14_en","",20385,], -["features.roomdetails.impl_RoomDetails_15_en","",20385,], -["features.roomdetails.impl_RoomDetails_16_en","",20385,], -["features.roomdetails.impl_RoomDetails_17_en","",20385,], -["features.roomdetails.impl_RoomDetails_18_en","",20385,], -["features.roomdetails.impl_RoomDetails_19_en","",20385,], -["features.roomdetails.impl_RoomDetails_1_en","",20385,], -["features.roomdetails.impl_RoomDetails_2_en","",20385,], -["features.roomdetails.impl_RoomDetails_3_en","",20385,], -["features.roomdetails.impl_RoomDetails_4_en","",20385,], -["features.roomdetails.impl_RoomDetails_5_en","",20385,], -["features.roomdetails.impl_RoomDetails_6_en","",20385,], -["features.roomdetails.impl_RoomDetails_7_en","",20385,], -["features.roomdetails.impl_RoomDetails_8_en","",20385,], -["features.roomdetails.impl_RoomDetails_9_en","",20385,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20385,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20385,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20385,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20385,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20385,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20385,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20385,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20385,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20385,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20392,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20392,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20392,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_0_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_0_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_1_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_1_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_2_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_2_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_3_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_3_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_4_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_4_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_5_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_5_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_6_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_6_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_7_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_7_en",20392,], +["features.roomdetails.impl.edit_RoomDetailsEditView_Day_8_en","features.roomdetails.impl.edit_RoomDetailsEditView_Night_8_en",20395,], +["features.roomdetails.impl_RoomDetails_0_en","",20392,], +["features.roomdetails.impl_RoomDetails_10_en","",20392,], +["features.roomdetails.impl_RoomDetails_11_en","",20392,], +["features.roomdetails.impl_RoomDetails_12_en","",20392,], +["features.roomdetails.impl_RoomDetails_13_en","",20392,], +["features.roomdetails.impl_RoomDetails_14_en","",20392,], +["features.roomdetails.impl_RoomDetails_15_en","",20392,], +["features.roomdetails.impl_RoomDetails_16_en","",20392,], +["features.roomdetails.impl_RoomDetails_17_en","",20392,], +["features.roomdetails.impl_RoomDetails_18_en","",20392,], +["features.roomdetails.impl_RoomDetails_19_en","",20392,], +["features.roomdetails.impl_RoomDetails_1_en","",20392,], +["features.roomdetails.impl_RoomDetails_2_en","",20392,], +["features.roomdetails.impl_RoomDetails_3_en","",20392,], +["features.roomdetails.impl_RoomDetails_4_en","",20392,], +["features.roomdetails.impl_RoomDetails_5_en","",20392,], +["features.roomdetails.impl_RoomDetails_6_en","",20392,], +["features.roomdetails.impl_RoomDetails_7_en","",20392,], +["features.roomdetails.impl_RoomDetails_8_en","",20392,], +["features.roomdetails.impl_RoomDetails_9_en","",20392,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20392,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20392,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20392,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20392,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20392,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20392,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20392,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20392,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20392,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20385,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20385,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20385,], -["features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Night_0_en",20385,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20385,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20385,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_0_en",20385,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_1_en",20385,], -["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_2_en",20385,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20392,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20392,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20392,], +["features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenuContent_Night_0_en",20392,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20392,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20392,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_0_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_0_en",20392,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_1_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_1_en",20392,], +["features.home.impl.roomlist_RoomListModalBottomSheetContent_Day_2_en","features.home.impl.roomlist_RoomListModalBottomSheetContent_Night_2_en",20392,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20385,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20385,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20385,], -["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20385,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20392,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_0_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_0_en",20392,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_1_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_1_en",20392,], +["features.roomdetails.impl.members_RoomMemberListViewBanned_Day_2_en","features.roomdetails.impl.members_RoomMemberListViewBanned_Night_2_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20392,], ["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",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20385,], -["features.roomdetails.impl.members_RoomMemberListView_Day_9_en","features.roomdetails.impl.members_RoomMemberListView_Night_9_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20385,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20385,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20385,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20385,], +["features.roomdetails.impl.members_RoomMemberListView_Day_7_en","features.roomdetails.impl.members_RoomMemberListView_Night_7_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_8_en","features.roomdetails.impl.members_RoomMemberListView_Night_8_en",20392,], +["features.roomdetails.impl.members_RoomMemberListView_Day_9_en","features.roomdetails.impl.members_RoomMemberListView_Night_9_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20392,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20392,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20392,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20392,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1016,14 +1017,14 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_26_en","features.home.impl.components_RoomSummaryRow_Night_26_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20385,], -["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20385,], +["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_33_en","features.home.impl.components_RoomSummaryRow_Night_33_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_34_en","features.home.impl.components_RoomSummaryRow_Night_34_en",20392,], +["features.home.impl.components_RoomSummaryRow_Day_35_en","features.home.impl.components_RoomSummaryRow_Night_35_en",20392,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_5_en","features.home.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -1031,82 +1032,82 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20385,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20385,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20385,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20392,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20392,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20392,], ["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",20385,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20385,], -["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20385,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20392,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_0_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_0_en",20392,], +["features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Day_1_en","features.login.impl.screens.searchaccountprovider_SearchAccountProviderView_Night_1_en",20392,], ["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","",20385,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20392,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20385,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20385,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20385,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20385,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20385,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20385,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20385,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20385,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20385,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20385,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20385,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20385,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_9_en","",20388,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20385,], -["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_9_en","",20388,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20392,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20392,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20392,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20392,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20392,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20392,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20392,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20392,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20392,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20392,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20392,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20392,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_0_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_1_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_2_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_3_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_4_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_5_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_6_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_7_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_8_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewDark_9_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_0_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_1_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_2_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_3_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_4_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_5_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_6_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_7_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_8_en","",20392,], +["features.roomdetails.impl.securityandprivacy_SecurityAndPrivacyViewLight_9_en","",20392,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1120,11 +1121,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_SelectedUser_Day_1_en","libraries.matrix.ui.components_SelectedUser_Night_1_en",0,], ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButton_Day_0_en","libraries.textcomposer.components_SendButton_Night_0_en",0,], -["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20385,], -["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20385,], -["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20385,], -["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20385,], -["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20385,], +["features.location.impl.send_SendLocationView_Day_0_en","features.location.impl.send_SendLocationView_Night_0_en",20392,], +["features.location.impl.send_SendLocationView_Day_1_en","features.location.impl.send_SendLocationView_Night_1_en",20392,], +["features.location.impl.send_SendLocationView_Day_2_en","features.location.impl.send_SendLocationView_Night_2_en",20392,], +["features.location.impl.send_SendLocationView_Day_3_en","features.location.impl.send_SendLocationView_Night_3_en",20392,], +["features.location.impl.send_SendLocationView_Day_4_en","features.location.impl.send_SendLocationView_Night_4_en",20392,], ["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,], @@ -1134,27 +1135,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",20385,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20385,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20385,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20385,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20385,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20385,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20385,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20385,], +["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20392,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20392,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20392,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20392,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20392,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20392,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20392,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20392,], ["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",20385,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20385,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20385,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20385,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20385,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20385,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20385,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20385,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20385,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20385,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20392,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20392,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20392,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20392,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20392,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20392,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20392,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20392,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20392,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20392,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["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,], @@ -1164,98 +1165,98 @@ 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",20385,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20392,], ["libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], -["features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en","features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en",20385,], +["features.announcement.impl.spaces_SpaceAnnouncementView_Day_0_en","features.announcement.impl.spaces_SpaceAnnouncementView_Night_0_en",20392,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20385,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20385,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20385,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20392,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20392,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20392,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20385,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20385,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20385,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20385,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20385,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20385,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20385,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20385,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20392,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20392,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20392,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20392,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20392,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20392,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20392,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20392,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20385,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20385,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20385,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20385,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20385,], -["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20385,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20385,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20392,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20392,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20392,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20392,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20392,], +["features.startchat.impl.root_StartChatView_Day_5_en","features.startchat.impl.root_StartChatView_Night_5_en",20392,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20392,], ["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",20385,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20392,], ["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",20385,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20392,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20385,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20385,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20385,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20385,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20385,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20385,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20385,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20385,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20385,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20385,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20385,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20385,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20385,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20385,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20385,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20392,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20392,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20392,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20392,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20392,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20392,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20392,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20392,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20392,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20392,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20392,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20392,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20392,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20392,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20392,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20385,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20385,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20392,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20392,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1267,16 +1268,16 @@ export const screenshots = [ ["libraries.mediaviewer.impl.local.txt_TextFileContentView_Day_3_en","libraries.mediaviewer.impl.local.txt_TextFileContentView_Night_3_en",0,], ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20385,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20385,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20385,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20385,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20385,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20392,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20392,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20392,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20392,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20392,], ["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",20385,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20385,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20392,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20392,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], @@ -1286,18 +1287,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",20385,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20385,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20392,], ["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,], @@ -1305,18 +1306,18 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20385,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20392,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20392,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20385,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20385,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20392,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20392,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20392,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20392,], ["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,], @@ -1325,41 +1326,41 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20392,], ["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",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20392,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20385,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20392,], ["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","",20385,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20392,], ["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",20385,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20385,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20392,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20392,], ["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",20385,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20385,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20385,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20385,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20392,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20385,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20392,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20392,], ["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",20385,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20392,], ["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,], @@ -1368,8 +1369,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",20385,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20385,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20392,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20392,], ["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,], @@ -1384,8 +1385,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",20385,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20385,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20392,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20392,], ["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,], @@ -1408,85 +1409,85 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Day_0_en","features.messages.impl.timeline.components.event_TimelineVideoWithCaptionRow_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20385,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20392,], ["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",20385,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20385,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20385,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20392,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20392,], ["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",20385,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20392,], ["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",20385,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20392,], ["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",20385,], +["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",20392,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_en","",0,], ["libraries.designsystem.theme.components_TopAppBar_App_Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20385,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20385,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20392,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20392,], ["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",20385,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20385,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20385,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20385,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20385,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20385,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20392,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20392,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20392,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20392,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20392,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20392,], ["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","",20385,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20392,], ["libraries.matrix.ui.components_UnsavedAvatar_Day_0_en","libraries.matrix.ui.components_UnsavedAvatar_Night_0_en",0,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20385,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20385,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20385,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20385,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20392,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20392,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20392,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20392,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20385,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20392,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20385,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20392,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], -["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20385,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20385,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20385,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20385,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20385,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20385,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20385,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20385,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20385,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20385,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20385,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20385,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20392,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20392,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20392,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20392,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20392,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20392,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20392,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20392,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20392,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20392,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20392,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20392,], ["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20385,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20385,], +["features.preferences.impl.advanced_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.advanced_VideoQualitySelectorDialog_Night_0_en",20392,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20392,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20385,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20392,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], diff --git a/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Day_3_en.png index c913d383ca..7866726430 100644 --- a/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c6ab936964323971e9b4927684e3ae2da96884b340569aebe6e6a711bd6c241 -size 8446 +oid sha256:9bd3fe043d7fffab7b19b5af9de7ef58ce8d2d97018a058b5b3c94cb55b277b9 +size 11273 diff --git a/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Night_3_en.png index 33799bf61d..ff6610c109 100644 --- a/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.forward.impl_ForwardMessagesView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd3611749f6ad60bc647236c5a10694b8ac9d1aa84e546740c1bf85df277c6f9 -size 7668 +oid sha256:97bc2d4c9621a21533da2d35abe90a5e1c1ef1bc356a6342736b61f7be8d8e3b +size 10357 diff --git a/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt b/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt index 37f0500444..c4357b2d3c 100644 --- a/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt +++ b/tools/templates/files/fileTemplates/Template Module Feature Entry Point API.kt @@ -6,12 +6,11 @@ import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint interface ${FEATURE_NAME}EntryPoint : FeatureEntryPoint { - fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder - - interface NodeBuilder { - fun callback(callback: Callback): NodeBuilder - fun build(): Node - } + fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: Callback, + ): Node interface Callback : Plugin { // Add your callbacks diff --git a/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt b/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt index 6eba997336..3642f8e821 100644 --- a/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt +++ b/tools/templates/files/fileTemplates/Template Module Feature Entry Point Flow Impl.kt @@ -11,19 +11,11 @@ import dev.zacsweers.metro.AppScope @ContributesBinding(AppScope::class) class Default${FEATURE_NAME}EntryPoint() : ${FEATURE_NAME}EntryPoint { - override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): ${FEATURE_NAME}EntryPoint.NodeBuilder { - val plugins = ArrayList() - - return object : ${FEATURE_NAME}EntryPoint.NodeBuilder { - - override fun callback(callback: ${FEATURE_NAME}EntryPoint.Callback): ${FEATURE_NAME}EntryPoint.NodeBuilder { - plugins += callback - return this - } - - override fun build(): Node { - return parentNode.createNode<${FEATURE_NAME}FlowNode>(buildContext, plugins) - } - } + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + callback: ${FEATURE_NAME}EntryPoint.Callback, + ): Node { + return parentNode.createNode<${FEATURE_NAME}FlowNode>(buildContext, listOf(callback)) } }