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 8fdfbf3bc0..4d6553f51c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -69,7 +69,6 @@ import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -292,6 +291,10 @@ class LoggedInFlowNode @AssistedInject constructor( override fun onOpenBugReport() { plugins().forEach { it.onOpenBugReport() } } + + override fun onVerifyClicked() { + backstack.push(NavTarget.VerifySession) + } } preferencesEntryPoint.nodeBuilder(this, buildContext) .callback(callback) 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 464ab2159f..3d1a516593 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 @@ -32,5 +32,6 @@ interface PreferencesEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun onOpenBugReport() + fun onVerifyClicked() } } 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 35fe4e77bb..e5b8254488 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 @@ -75,6 +75,10 @@ class PreferencesFlowNode @AssistedInject constructor( plugins().forEach { it.onOpenBugReport() } } + override fun onVerifyClicked() { + plugins().forEach { it.onVerifyClicked() } + } + override fun onOpenAnalytics() { backstack.push(NavTarget.AnalyticsSettings) } 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 d0920e4fe6..a564927101 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 @@ -36,6 +36,7 @@ class PreferencesRootNode @AssistedInject constructor( interface Callback : Plugin { fun onOpenBugReport() + fun onVerifyClicked() fun onOpenAnalytics() fun onOpenAbout() fun onOpenDeveloperSettings() @@ -45,6 +46,10 @@ class PreferencesRootNode @AssistedInject constructor( plugins().forEach { it.onOpenBugReport() } } + private fun onVerifyClicked() { + plugins().forEach { it.onVerifyClicked() } + } + private fun onOpenDeveloperSettings() { plugins().forEach { it.onOpenDeveloperSettings() } } @@ -67,6 +72,7 @@ class PreferencesRootNode @AssistedInject constructor( onOpenRageShake = this::onOpenBugReport, onOpenAnalytics = this::onOpenAnalytics, onOpenAbout = this::onOpenAbout, + onVerifyClicked = this::onVerifyClicked, onOpenDeveloperSettings = this::onOpenDeveloperSettings ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt index 2fe22161a8..211b7d4c3e 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt @@ -19,13 +19,19 @@ package io.element.android.features.preferences.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.logout.api.LogoutPreferencePresenter import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.matrix.api.user.CurrentUserProvider import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.api.verification.SessionVerificationService +import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject @@ -33,6 +39,7 @@ import javax.inject.Inject class PreferencesRootPresenter @Inject constructor( private val logoutPresenter: LogoutPreferencePresenter, private val currentUserProvider: CurrentUserProvider, + private val sessionVerificationService: SessionVerificationService, private val buildType: BuildType, ) : Presenter { @@ -45,11 +52,18 @@ class PreferencesRootPresenter @Inject constructor( initialLoad(matrixUser) } + // Session verification status (unknown, not verified, verified) + val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() + val sessionIsNotVerified by remember { + derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified } + } + val logoutState = logoutPresenter.present() val showDeveloperSettings = buildType != BuildType.RELEASE return PreferencesRootState( logoutState = logoutState, myUser = matrixUser.value, + showCompleteVerification = sessionIsNotVerified, showDeveloperSettings = showDeveloperSettings ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt index d1e3a0535c..c412eadde1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootState.kt @@ -22,5 +22,6 @@ import io.element.android.libraries.matrix.api.user.MatrixUser data class PreferencesRootState( val logoutState: LogoutPreferenceState, val myUser: MatrixUser?, + val showCompleteVerification: Boolean, val showDeveloperSettings: Boolean ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt index c438687278..051800e8cd 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootStateProvider.kt @@ -21,5 +21,6 @@ import io.element.android.features.logout.api.aLogoutPreferenceState fun aPreferencesRootState() = PreferencesRootState( logoutState = aLogoutPreferenceState(), myUser = null, + showCompleteVerification = true, showDeveloperSettings = true ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index a652b866ad..07a4fca15f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -18,9 +18,10 @@ package io.element.android.features.preferences.impl.root import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DeveloperMode -import androidx.compose.material.icons.filled.Help import androidx.compose.material.icons.outlined.BugReport +import androidx.compose.material.icons.outlined.Help import androidx.compose.material.icons.outlined.InsertChart +import androidx.compose.material.icons.outlined.VerifiedUser import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -42,6 +43,7 @@ fun PreferencesRootView( state: PreferencesRootState, modifier: Modifier = Modifier, onBackPressed: () -> Unit, + onVerifyClicked: () -> Unit, onOpenAnalytics: () -> Unit, onOpenRageShake: () -> Unit, onOpenAbout: () -> Unit, @@ -54,7 +56,14 @@ fun PreferencesRootView( title = stringResource(id = CommonStrings.common_settings) ) { UserPreferences(state.myUser) - // TODO Verification and eventually divider + if (state.showCompleteVerification) { + PreferenceText( + title = stringResource(id = CommonStrings.action_complete_verification), + icon = Icons.Outlined.VerifiedUser, + onClick = onVerifyClicked, + ) + Divider() + } PreferenceText( title = stringResource(id = CommonStrings.common_analytics), icon = Icons.Outlined.InsertChart, @@ -67,7 +76,7 @@ fun PreferencesRootView( ) PreferenceText( title = stringResource(id = CommonStrings.common_about), - icon = Icons.Filled.Help, + icon = Icons.Outlined.Help, onClick = onOpenAbout, ) if (state.showDeveloperSettings) { @@ -108,5 +117,6 @@ private fun ContentToPreview(matrixUser: MatrixUser) { onOpenRageShake = {}, onOpenDeveloperSettings = {}, onOpenAbout = {}, + onVerifyClicked = {}, ) } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt index 4254668583..040fda17d1 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt @@ -20,19 +20,15 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.features.analytics.impl.preferences.DefaultAnalyticsPreferencesPresenter import io.element.android.features.analytics.test.A_BUILD_META -import io.element.android.features.analytics.test.FakeAnalyticsService import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter -import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter -import io.element.android.features.rageshake.test.rageshake.FakeRageShake -import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.user.CurrentUserProvider import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_AVATAR_URL import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient +import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService import kotlinx.coroutines.test.runTest import org.junit.Test @@ -44,6 +40,7 @@ class PreferencesRootPresenterTest { val presenter = PreferencesRootPresenter( logoutPresenter, CurrentUserProvider(matrixClient), + FakeSessionVerificationService(), A_BUILD_META.buildType ) moleculeFlow(RecompositionClock.Immediate) {