Add "Complete verification" item in the root setting screen.

This commit is contained in:
Benoit Marty 2023-06-30 16:35:09 +02:00 committed by Benoit Marty
parent 7b5d18aef4
commit acec682dee
9 changed files with 46 additions and 9 deletions

View file

@ -69,7 +69,6 @@ import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.appnavstate.api.AppNavigationStateService import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -292,6 +291,10 @@ class LoggedInFlowNode @AssistedInject constructor(
override fun onOpenBugReport() { override fun onOpenBugReport() {
plugins<Callback>().forEach { it.onOpenBugReport() } plugins<Callback>().forEach { it.onOpenBugReport() }
} }
override fun onVerifyClicked() {
backstack.push(NavTarget.VerifySession)
}
} }
preferencesEntryPoint.nodeBuilder(this, buildContext) preferencesEntryPoint.nodeBuilder(this, buildContext)
.callback(callback) .callback(callback)

View file

@ -32,5 +32,6 @@ interface PreferencesEntryPoint : FeatureEntryPoint {
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenBugReport() fun onOpenBugReport()
fun onVerifyClicked()
} }
} }

View file

@ -75,6 +75,10 @@ class PreferencesFlowNode @AssistedInject constructor(
plugins<PreferencesEntryPoint.Callback>().forEach { it.onOpenBugReport() } plugins<PreferencesEntryPoint.Callback>().forEach { it.onOpenBugReport() }
} }
override fun onVerifyClicked() {
plugins<PreferencesEntryPoint.Callback>().forEach { it.onVerifyClicked() }
}
override fun onOpenAnalytics() { override fun onOpenAnalytics() {
backstack.push(NavTarget.AnalyticsSettings) backstack.push(NavTarget.AnalyticsSettings)
} }

View file

@ -36,6 +36,7 @@ class PreferencesRootNode @AssistedInject constructor(
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenBugReport() fun onOpenBugReport()
fun onVerifyClicked()
fun onOpenAnalytics() fun onOpenAnalytics()
fun onOpenAbout() fun onOpenAbout()
fun onOpenDeveloperSettings() fun onOpenDeveloperSettings()
@ -45,6 +46,10 @@ class PreferencesRootNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onOpenBugReport() } plugins<Callback>().forEach { it.onOpenBugReport() }
} }
private fun onVerifyClicked() {
plugins<Callback>().forEach { it.onVerifyClicked() }
}
private fun onOpenDeveloperSettings() { private fun onOpenDeveloperSettings() {
plugins<Callback>().forEach { it.onOpenDeveloperSettings() } plugins<Callback>().forEach { it.onOpenDeveloperSettings() }
} }
@ -67,6 +72,7 @@ class PreferencesRootNode @AssistedInject constructor(
onOpenRageShake = this::onOpenBugReport, onOpenRageShake = this::onOpenBugReport,
onOpenAnalytics = this::onOpenAnalytics, onOpenAnalytics = this::onOpenAnalytics,
onOpenAbout = this::onOpenAbout, onOpenAbout = this::onOpenAbout,
onVerifyClicked = this::onVerifyClicked,
onOpenDeveloperSettings = this::onOpenDeveloperSettings onOpenDeveloperSettings = this::onOpenDeveloperSettings
) )
} }

View file

@ -19,13 +19,19 @@ package io.element.android.features.preferences.impl.root
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState 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.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.logout.api.LogoutPreferencePresenter import io.element.android.features.logout.api.LogoutPreferencePresenter
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.meta.BuildType 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.CurrentUserProvider
import io.element.android.libraries.matrix.api.user.MatrixUser 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.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -33,6 +39,7 @@ import javax.inject.Inject
class PreferencesRootPresenter @Inject constructor( class PreferencesRootPresenter @Inject constructor(
private val logoutPresenter: LogoutPreferencePresenter, private val logoutPresenter: LogoutPreferencePresenter,
private val currentUserProvider: CurrentUserProvider, private val currentUserProvider: CurrentUserProvider,
private val sessionVerificationService: SessionVerificationService,
private val buildType: BuildType, private val buildType: BuildType,
) : Presenter<PreferencesRootState> { ) : Presenter<PreferencesRootState> {
@ -45,11 +52,18 @@ class PreferencesRootPresenter @Inject constructor(
initialLoad(matrixUser) 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 logoutState = logoutPresenter.present()
val showDeveloperSettings = buildType != BuildType.RELEASE val showDeveloperSettings = buildType != BuildType.RELEASE
return PreferencesRootState( return PreferencesRootState(
logoutState = logoutState, logoutState = logoutState,
myUser = matrixUser.value, myUser = matrixUser.value,
showCompleteVerification = sessionIsNotVerified,
showDeveloperSettings = showDeveloperSettings showDeveloperSettings = showDeveloperSettings
) )
} }

View file

@ -22,5 +22,6 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
data class PreferencesRootState( data class PreferencesRootState(
val logoutState: LogoutPreferenceState, val logoutState: LogoutPreferenceState,
val myUser: MatrixUser?, val myUser: MatrixUser?,
val showCompleteVerification: Boolean,
val showDeveloperSettings: Boolean val showDeveloperSettings: Boolean
) )

View file

@ -21,5 +21,6 @@ import io.element.android.features.logout.api.aLogoutPreferenceState
fun aPreferencesRootState() = PreferencesRootState( fun aPreferencesRootState() = PreferencesRootState(
logoutState = aLogoutPreferenceState(), logoutState = aLogoutPreferenceState(),
myUser = null, myUser = null,
showCompleteVerification = true,
showDeveloperSettings = true showDeveloperSettings = true
) )

View file

@ -18,9 +18,10 @@ package io.element.android.features.preferences.impl.root
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DeveloperMode 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.BugReport
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.InsertChart import androidx.compose.material.icons.outlined.InsertChart
import androidx.compose.material.icons.outlined.VerifiedUser
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -42,6 +43,7 @@ fun PreferencesRootView(
state: PreferencesRootState, state: PreferencesRootState,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBackPressed: () -> Unit, onBackPressed: () -> Unit,
onVerifyClicked: () -> Unit,
onOpenAnalytics: () -> Unit, onOpenAnalytics: () -> Unit,
onOpenRageShake: () -> Unit, onOpenRageShake: () -> Unit,
onOpenAbout: () -> Unit, onOpenAbout: () -> Unit,
@ -54,7 +56,14 @@ fun PreferencesRootView(
title = stringResource(id = CommonStrings.common_settings) title = stringResource(id = CommonStrings.common_settings)
) { ) {
UserPreferences(state.myUser) 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( PreferenceText(
title = stringResource(id = CommonStrings.common_analytics), title = stringResource(id = CommonStrings.common_analytics),
icon = Icons.Outlined.InsertChart, icon = Icons.Outlined.InsertChart,
@ -67,7 +76,7 @@ fun PreferencesRootView(
) )
PreferenceText( PreferenceText(
title = stringResource(id = CommonStrings.common_about), title = stringResource(id = CommonStrings.common_about),
icon = Icons.Filled.Help, icon = Icons.Outlined.Help,
onClick = onOpenAbout, onClick = onOpenAbout,
) )
if (state.showDeveloperSettings) { if (state.showDeveloperSettings) {
@ -108,5 +117,6 @@ private fun ContentToPreview(matrixUser: MatrixUser) {
onOpenRageShake = {}, onOpenRageShake = {},
onOpenDeveloperSettings = {}, onOpenDeveloperSettings = {},
onOpenAbout = {}, onOpenAbout = {},
onVerifyClicked = {},
) )
} }

View file

@ -20,19 +20,15 @@ import app.cash.molecule.RecompositionClock
import app.cash.molecule.moleculeFlow import app.cash.molecule.moleculeFlow
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat 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.A_BUILD_META
import io.element.android.features.analytics.test.FakeAnalyticsService
import io.element.android.features.logout.impl.DefaultLogoutPreferencePresenter 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.architecture.Async
import io.element.android.libraries.matrix.api.user.CurrentUserProvider 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.user.MatrixUser
import io.element.android.libraries.matrix.test.AN_AVATAR_URL 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.A_USER_NAME
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@ -44,6 +40,7 @@ class PreferencesRootPresenterTest {
val presenter = PreferencesRootPresenter( val presenter = PreferencesRootPresenter(
logoutPresenter, logoutPresenter,
CurrentUserProvider(matrixClient), CurrentUserProvider(matrixClient),
FakeSessionVerificationService(),
A_BUILD_META.buildType A_BUILD_META.buildType
) )
moleculeFlow(RecompositionClock.Immediate) { moleculeFlow(RecompositionClock.Immediate) {