mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge pull request #5325 from vector-im/feature/eric/registration-feature-flag
#5307 Adds ForceLoginFallback feature flag to Login and Registration
This commit is contained in:
commit
27905064e0
11 changed files with 137 additions and 72 deletions
1
changelog.d/5325.feature
Normal file
1
changelog.d/5325.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Adds forceLoginFallback feature flag and usages to FTUE login and registration
|
|
@ -53,7 +53,7 @@ class DebugFeaturesStateFactory @Inject constructor(
|
|||
label = "FTUE Personalize profile",
|
||||
key = DebugFeatureKeys.onboardingPersonalize,
|
||||
factory = VectorFeatures::isOnboardingPersonalizeEnabled
|
||||
)
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -43,9 +43,13 @@ class DebugPrivateSettingsFragment : VectorBaseFragment<FragmentDebugPrivateSett
|
|||
views.forceDialPadTabDisplay.setOnCheckedChangeListener { _, isChecked ->
|
||||
viewModel.handle(DebugPrivateSettingsViewActions.SetDialPadVisibility(isChecked))
|
||||
}
|
||||
views.forceLoginFallback.setOnCheckedChangeListener { _, isChecked ->
|
||||
viewModel.handle(DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled(isChecked))
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
views.forceDialPadTabDisplay.isChecked = it.dialPadVisible
|
||||
views.forceLoginFallback.isChecked = it.forceLoginFallback
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewModelAction
|
|||
|
||||
sealed class DebugPrivateSettingsViewActions : VectorViewModelAction {
|
||||
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||
}
|
||||
|
|
|
@ -45,15 +45,18 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||
|
||||
private fun observeVectorDataStore() {
|
||||
vectorDataStore.forceDialPadDisplayFlow.setOnEach {
|
||||
copy(
|
||||
dialPadVisible = it
|
||||
)
|
||||
copy(dialPadVisible = it)
|
||||
}
|
||||
|
||||
vectorDataStore.forceLoginFallbackFlow.setOnEach {
|
||||
copy(forceLoginFallback = it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: DebugPrivateSettingsViewActions) {
|
||||
when (action) {
|
||||
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
|
||||
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
|
||||
is DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled -> handleSetForceLoginFallbackEnabled(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,4 +65,10 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||
vectorDataStore.setForceDialPadDisplay(action.force)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetForceLoginFallbackEnabled(action: DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled) {
|
||||
viewModelScope.launch {
|
||||
vectorDataStore.setForceLoginFallbackFlow(action.force)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,5 +19,6 @@ package im.vector.app.features.debug.settings
|
|||
import com.airbnb.mvrx.MavericksState
|
||||
|
||||
data class DebugPrivateSettingsViewState(
|
||||
val dialPadVisible: Boolean = false
|
||||
val dialPadVisible: Boolean = false,
|
||||
val forceLoginFallback: Boolean = false,
|
||||
) : MavericksState
|
||||
|
|
|
@ -25,6 +25,12 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="Force DialPad tab display" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/forceLoginFallback"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Force login and registration fallback" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
|
@ -46,6 +46,7 @@ import im.vector.app.features.login.LoginMode
|
|||
import im.vector.app.features.login.ReAuthHelper
|
||||
import im.vector.app.features.login.ServerType
|
||||
import im.vector.app.features.login.SignMode
|
||||
import im.vector.app.features.settings.VectorDataStore
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixPatterns.getDomain
|
||||
|
@ -78,7 +79,8 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
private val stringProvider: StringProvider,
|
||||
private val homeServerHistoryService: HomeServerHistoryService,
|
||||
private val vectorFeatures: VectorFeatures,
|
||||
private val analyticsTracker: AnalyticsTracker
|
||||
private val analyticsTracker: AnalyticsTracker,
|
||||
private val vectorDataStore: VectorDataStore,
|
||||
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -90,6 +92,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
|
||||
init {
|
||||
getKnownCustomHomeServersUrls()
|
||||
observeDataStore()
|
||||
}
|
||||
|
||||
private fun getKnownCustomHomeServersUrls() {
|
||||
|
@ -98,6 +101,12 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun observeDataStore() = viewModelScope.launch {
|
||||
vectorDataStore.forceLoginFallbackFlow.setOnEach { isForceLoginFallbackEnabled ->
|
||||
copy(isForceLoginFallbackEnabled = isForceLoginFallbackEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
// Store the last action, to redo it after user has trusted the untrusted certificate
|
||||
private var lastAction: OnboardingAction? = null
|
||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
||||
|
|
|
@ -62,7 +62,8 @@ data class OnboardingViewState(
|
|||
// Supported types for the login. We cannot use a sealed class for LoginType because it is not serializable
|
||||
@PersistState
|
||||
val loginModeSupportedTypes: List<String> = emptyList(),
|
||||
val knownCustomHomeServersUrls: List<String> = emptyList()
|
||||
val knownCustomHomeServersUrls: List<String> = emptyList(),
|
||||
val isForceLoginFallbackEnabled: Boolean = false,
|
||||
) : MavericksState {
|
||||
|
||||
fun isLoading(): Boolean {
|
||||
|
|
|
@ -75,6 +75,8 @@ class FtueAuthVariant(
|
|||
private val popEnterAnim = R.anim.no_anim
|
||||
private val popExitAnim = R.anim.exit_fade_out
|
||||
|
||||
private var isForceLoginFallbackEnabled = false
|
||||
|
||||
private val topFragment: Fragment?
|
||||
get() = supportFragmentManager.findFragmentById(views.loginFragmentContainer.id)
|
||||
|
||||
|
@ -109,10 +111,6 @@ class FtueAuthVariant(
|
|||
}
|
||||
}
|
||||
|
||||
override fun setIsLoading(isLoading: Boolean) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private fun addFirstFragment() {
|
||||
val splashFragment = when (vectorFeatures.isOnboardingSplashCarouselEnabled()) {
|
||||
true -> FtueAuthSplashCarouselFragment::class.java
|
||||
|
@ -121,11 +119,25 @@ class FtueAuthVariant(
|
|||
activity.addFragment(views.loginFragmentContainer, splashFragment)
|
||||
}
|
||||
|
||||
private fun updateWithState(viewState: OnboardingViewState) {
|
||||
isForceLoginFallbackEnabled = viewState.isForceLoginFallbackEnabled
|
||||
views.loginLoading.isVisible = shouldShowLoading(viewState)
|
||||
}
|
||||
|
||||
private fun shouldShowLoading(viewState: OnboardingViewState) =
|
||||
if (vectorFeatures.isOnboardingPersonalizeEnabled()) {
|
||||
viewState.isLoading()
|
||||
} else {
|
||||
// Keep loading when during success because of the delay when switching to the next Activity
|
||||
viewState.isLoading() || viewState.isAuthTaskCompleted()
|
||||
}
|
||||
|
||||
override fun setIsLoading(isLoading: Boolean) = Unit
|
||||
|
||||
private fun handleOnboardingViewEvents(viewEvents: OnboardingViewEvents) {
|
||||
when (viewEvents) {
|
||||
is OnboardingViewEvents.RegistrationFlowResult -> {
|
||||
// Check that all flows are supported by the application
|
||||
if (viewEvents.flowResult.missingStages.any { !it.isSupported() }) {
|
||||
if (registrationShouldFallback(viewEvents)) {
|
||||
// Display a popup to propose use web fallback
|
||||
onRegistrationStageNotSupported()
|
||||
} else {
|
||||
|
@ -136,11 +148,7 @@ class FtueAuthVariant(
|
|||
// First ask for login and password
|
||||
// I add a tag to indicate that this fragment is a registration stage.
|
||||
// This way it will be automatically popped in when starting the next registration stage
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthLoginFragment::class.java,
|
||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
||||
option = commonOption
|
||||
)
|
||||
openAuthLoginFragmentWithTag(FRAGMENT_REGISTRATION_STAGE_TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,13 +236,23 @@ class FtueAuthVariant(
|
|||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun updateWithState(viewState: OnboardingViewState) {
|
||||
views.loginLoading.isVisible = if (vectorFeatures.isOnboardingPersonalizeEnabled()) {
|
||||
viewState.isLoading()
|
||||
} else {
|
||||
// Keep loading when during success because of the delay when switching to the next Activity
|
||||
viewState.isLoading() || viewState.isAuthTaskCompleted()
|
||||
}
|
||||
private fun registrationShouldFallback(registrationFlowResult: OnboardingViewEvents.RegistrationFlowResult) =
|
||||
isForceLoginFallbackEnabled || registrationFlowResult.containsUnsupportedRegistrationFlow()
|
||||
|
||||
private fun OnboardingViewEvents.RegistrationFlowResult.containsUnsupportedRegistrationFlow() =
|
||||
flowResult.missingStages.any { !it.isSupported() }
|
||||
|
||||
private fun onRegistrationStageNotSupported() {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.app_name)
|
||||
.setMessage(activity.getString(R.string.login_registration_not_supported))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthWebFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun onWebLoginError(onWebLoginError: OnboardingViewEvents.OnWebLoginError) {
|
||||
|
@ -264,29 +282,58 @@ class FtueAuthVariant(
|
|||
// state.signMode could not be ready yet. So use value from the ViewEvent
|
||||
when (OnboardingViewEvents.signMode) {
|
||||
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
|
||||
SignMode.SignUp -> {
|
||||
// This is managed by the OnboardingViewEvents
|
||||
}
|
||||
SignMode.SignIn -> {
|
||||
// It depends on the LoginMode
|
||||
when (state.loginMode) {
|
||||
LoginMode.Unknown,
|
||||
is LoginMode.Sso -> error("Developer error")
|
||||
is LoginMode.SsoAndPassword,
|
||||
LoginMode.Password -> activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthLoginFragment::class.java,
|
||||
tag = FRAGMENT_LOGIN_TAG,
|
||||
option = commonOption)
|
||||
LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes)
|
||||
}.exhaustive
|
||||
}
|
||||
SignMode.SignInWithMatrixId -> activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthLoginFragment::class.java,
|
||||
tag = FRAGMENT_LOGIN_TAG,
|
||||
option = commonOption)
|
||||
SignMode.SignUp -> Unit // This case is processed in handleOnboardingViewEvents
|
||||
SignMode.SignIn -> handleSignInSelected(state)
|
||||
SignMode.SignInWithMatrixId -> handleSignInWithMatrixId(state)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleSignInSelected(state: OnboardingViewState) {
|
||||
if (isForceLoginFallbackEnabled) {
|
||||
onLoginModeNotSupported(state.loginModeSupportedTypes)
|
||||
} else {
|
||||
disambiguateLoginMode(state)
|
||||
}
|
||||
}
|
||||
|
||||
private fun disambiguateLoginMode(state: OnboardingViewState) = when (state.loginMode) {
|
||||
LoginMode.Unknown,
|
||||
is LoginMode.Sso -> error("Developer error")
|
||||
is LoginMode.SsoAndPassword,
|
||||
LoginMode.Password -> openAuthLoginFragmentWithTag(FRAGMENT_LOGIN_TAG)
|
||||
LoginMode.Unsupported -> onLoginModeNotSupported(state.loginModeSupportedTypes)
|
||||
}
|
||||
|
||||
private fun openAuthLoginFragmentWithTag(tag: String) {
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthLoginFragment::class.java,
|
||||
tag = tag,
|
||||
option = commonOption)
|
||||
}
|
||||
|
||||
private fun onLoginModeNotSupported(supportedTypes: List<String>) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.app_name)
|
||||
.setMessage(activity.getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" }))
|
||||
.setPositiveButton(R.string.yes) { _, _ -> openAuthWebFragment() }
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun handleSignInWithMatrixId(state: OnboardingViewState) {
|
||||
if (isForceLoginFallbackEnabled) {
|
||||
onLoginModeNotSupported(state.loginModeSupportedTypes)
|
||||
} else {
|
||||
openAuthLoginFragmentWithTag(FRAGMENT_LOGIN_TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openAuthWebFragment() {
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthWebFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the SSO redirection here
|
||||
*/
|
||||
|
@ -296,32 +343,6 @@ class FtueAuthVariant(
|
|||
?.let { onboardingViewModel.handle(OnboardingAction.LoginWithToken(it)) }
|
||||
}
|
||||
|
||||
private fun onRegistrationStageNotSupported() {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.app_name)
|
||||
.setMessage(activity.getString(R.string.login_registration_not_supported))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthWebFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun onLoginModeNotSupported(supportedTypes: List<String>) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.app_name)
|
||||
.setMessage(activity.getString(R.string.login_mode_not_supported, supportedTypes.joinToString { "'$it'" }))
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
FtueAuthWebFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
.setNegativeButton(R.string.no, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun handleRegistrationNavigation(flowResult: FlowResult) {
|
||||
// Complete all mandatory stages first
|
||||
val mandatoryStage = flowResult.missingStages.firstOrNull { it.mandatory }
|
||||
|
|
|
@ -59,4 +59,16 @@ class VectorDataStore @Inject constructor(
|
|||
settings[forceDialPadDisplay] = force
|
||||
}
|
||||
}
|
||||
|
||||
private val forceLoginFallback = booleanPreferencesKey("force_login_fallback")
|
||||
|
||||
val forceLoginFallbackFlow: Flow<Boolean> = context.dataStore.data.map { preferences ->
|
||||
preferences[forceLoginFallback].orFalse()
|
||||
}
|
||||
|
||||
suspend fun setForceLoginFallbackFlow(force: Boolean) {
|
||||
context.dataStore.edit { settings ->
|
||||
settings[forceLoginFallback] = force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue