diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt index 6ebbffa6e3..d6c0daa26e 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt @@ -239,31 +239,19 @@ class OnboardingViewModel @AssistedInject constructor( val safeLoginWizard = loginWizard if (safeLoginWizard == null) { - setState { - copy( - asyncLoginAction = Fail(Throwable("Bad configuration")) - ) - } + setState { copy(isLoading = false) } + _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Bad configuration"))) } else { - setState { - copy( - asyncLoginAction = Loading() - ) - } + setState { copy(isLoading = true) } currentJob = viewModelScope.launch { try { - safeLoginWizard.loginWithToken(action.loginToken) + val result = safeLoginWizard.loginWithToken(action.loginToken) + onSessionCreated(result, isAccountCreated = false) } catch (failure: Throwable) { _viewEvents.post(OnboardingViewEvents.Failure(failure)) - setState { - copy( - asyncLoginAction = Fail(failure) - ) - } - null + setState { copy(isLoading = false) } } - ?.let { onSessionCreated(it, isAccountCreated = false) } } } } @@ -271,7 +259,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleRegisterAction(action: RegisterAction) { currentJob = viewModelScope.launch { if (action.hasLoadingState()) { - setState { copy(asyncRegistration = Loading()) } + setState { copy(isLoading = true) } } runCatching { registrationActionHandler.handleRegisterAction(registrationWizard, action) } .fold( @@ -292,7 +280,7 @@ class OnboardingViewModel @AssistedInject constructor( } } ) - setState { copy(asyncRegistration = Uninitialized) } + setState { copy(isLoading = false) } } } @@ -345,12 +333,7 @@ class OnboardingViewModel @AssistedInject constructor( OnboardingAction.ResetLogin -> { viewModelScope.launch { authenticationService.cancelPendingLoginOrRegistration() - setState { - copy( - asyncLoginAction = Uninitialized, - asyncRegistration = Uninitialized - ) - } + setState { copy(isLoading = false) } } } OnboardingAction.ResetResetPassword -> { @@ -515,11 +498,7 @@ class OnboardingViewModel @AssistedInject constructor( } private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) { - setState { - copy( - asyncLoginAction = Loading() - ) - } + setState { copy(isLoading = true) } currentJob = viewModelScope.launch { val data = try { @@ -546,11 +525,7 @@ class OnboardingViewModel @AssistedInject constructor( } private fun onWellKnownError() { - setState { - copy( - asyncLoginAction = Uninitialized - ) - } + setState { copy(isLoading = false) } _viewEvents.post(OnboardingViewEvents.Failure(Exception(stringProvider.getString(R.string.autodiscover_well_known_error)))) } @@ -587,18 +562,10 @@ class OnboardingViewModel @AssistedInject constructor( is Failure.UnrecognizedCertificateFailure -> { // Display this error in a dialog _viewEvents.post(OnboardingViewEvents.Failure(failure)) - setState { - copy( - asyncLoginAction = Uninitialized - ) - } + setState { copy(isLoading = false) } } else -> { - setState { - copy( - asyncLoginAction = Fail(failure) - ) - } + setState { copy(isLoading = false) } } } } @@ -607,37 +574,22 @@ class OnboardingViewModel @AssistedInject constructor( val safeLoginWizard = loginWizard if (safeLoginWizard == null) { - setState { - copy( - asyncLoginAction = Fail(Throwable("Bad configuration")) - ) - } + setState { copy(isLoading = false) } + _viewEvents.post(OnboardingViewEvents.Failure(Throwable("Bad configuration"))) } else { - setState { - copy( - asyncLoginAction = Loading() - ) - } - + setState { copy(isLoading = true) } currentJob = viewModelScope.launch { try { - safeLoginWizard.login( + val result = safeLoginWizard.login( action.username, action.password, action.initialDeviceName ) + reAuthHelper.data = action.password + onSessionCreated(result, isAccountCreated = false) } catch (failure: Throwable) { - setState { - copy( - asyncLoginAction = Fail(failure) - ) - } - null + setState { copy(isLoading = false) } } - ?.let { - reAuthHelper.data = action.password - onSessionCreated(it, isAccountCreated = false) - } } } } @@ -678,12 +630,12 @@ class OnboardingViewModel @AssistedInject constructor( true -> { val personalizationState = createPersonalizationState(session, state) setState { - copy(asyncLoginAction = Success(Unit), personalizationState = personalizationState) + copy(isLoading = false, personalizationState = personalizationState) } _viewEvents.post(OnboardingViewEvents.OnAccountCreated) } false -> { - setState { copy(asyncLoginAction = Success(Unit)) } + setState { copy(isLoading = false) } _viewEvents.post(OnboardingViewEvents.OnAccountSignedIn) } } @@ -712,14 +664,11 @@ class OnboardingViewModel @AssistedInject constructor( } else { currentJob = viewModelScope.launch { try { - authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials) + val result = authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials) + onSessionCreated(result, isAccountCreated = false) } catch (failure: Throwable) { - setState { - copy(asyncLoginAction = Fail(failure)) - } - null + setState { copy(isLoading = false) } } - ?.let { onSessionCreated(it, isAccountCreated = false) } } } } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt index cdb68cfdac..063c651dfd 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt @@ -29,11 +29,9 @@ import im.vector.app.features.login.SignMode import kotlinx.parcelize.Parcelize data class OnboardingViewState( - val asyncLoginAction: Async = Uninitialized, val asyncHomeServerLoginFlowRequest: Async = Uninitialized, val asyncResetPassword: Async = Uninitialized, val asyncResetMailConfirmed: Async = Uninitialized, - val asyncRegistration: Async = Uninitialized, val isLoading: Boolean = false, @PersistState @@ -73,11 +71,9 @@ data class OnboardingViewState( ) : MavericksState { fun legacyIsLoading(): Boolean { - return asyncLoginAction is Loading || - asyncHomeServerLoginFlowRequest is Loading || + return asyncHomeServerLoginFlowRequest is Loading || asyncResetPassword is Loading || - asyncResetMailConfirmed is Loading || - asyncRegistration is Loading + asyncResetMailConfirmed is Loading } } diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt index dacd8feab3..a5536b1dcd 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthLoginFragment.kt @@ -26,8 +26,6 @@ import androidx.autofill.HintConstants import androidx.core.text.isDigitsOnly import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope -import com.airbnb.mvrx.Fail -import com.airbnb.mvrx.Loading import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hidePassword @@ -74,6 +72,33 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment< override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.viewEvents + .stream() + .onEach { + when (it) { + is OnboardingViewEvents.Failure -> { + if (it.throwable is Failure.ServerError && + it.throwable.error.code == MatrixError.M_FORBIDDEN && + it.throwable.error.message.isEmpty()) { + // Login with email, but email unknown + views.loginFieldTil.error = getString(R.string.login_login_with_email_error) + } else { + // Trick to display the error without text. + views.loginFieldTil.error = " " + if (it.throwable.isInvalidPassword() && spaceInPassword()) { + views.passwordFieldTil.error = getString(R.string.auth_invalid_login_param_space_in_password) + } else { + views.passwordFieldTil.error = errorFormatter.toHumanReadable(it.throwable) + } + } + } + else -> { + // do nothing + } + } + } + .launchIn(lifecycleScope) + setupSubmitButton() setupForgottenPasswordButton() @@ -274,39 +299,9 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment< setupSocialLoginButtons(state) setupButtons(state) - when (state.asyncLoginAction) { - is Loading -> { - // Ensure password is hidden - views.passwordField.hidePassword() - } - is Fail -> { - val error = state.asyncLoginAction.error - if (error is Failure.ServerError && - error.error.code == MatrixError.M_FORBIDDEN && - error.error.message.isEmpty()) { - // Login with email, but email unknown - views.loginFieldTil.error = getString(R.string.login_login_with_email_error) - } else { - // Trick to display the error without text. - views.loginFieldTil.error = " " - if (error.isInvalidPassword() && spaceInPassword()) { - views.passwordFieldTil.error = getString(R.string.auth_invalid_login_param_space_in_password) - } else { - views.passwordFieldTil.error = errorFormatter.toHumanReadable(error) - } - } - } - // Success is handled by the LoginActivity - else -> Unit - } - - when (state.asyncRegistration) { - is Loading -> { - // Ensure password is hidden - views.passwordField.hidePassword() - } - // Success is handled by the LoginActivity - else -> Unit + if (state.isLoading) { + // Ensure password is hidden + views.passwordField.hidePassword() } } diff --git a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt index 20e6ceb55d..09cefc5ff2 100644 --- a/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/onboarding/OnboardingViewModelTest.kt @@ -128,8 +128,8 @@ class OnboardingViewModelTest { .assertStatesChanges( initialState, { copy(signMode = SignMode.SignUp) }, - { copy(asyncRegistration = Loading()) }, - { copy(asyncRegistration = Uninitialized) } + { copy(isLoading = true) }, + { copy(isLoading = false) } ) .assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true)) .finish() @@ -145,8 +145,8 @@ class OnboardingViewModelTest { test .assertStatesChanges( initialState, - { copy(asyncRegistration = Loading()) }, - { copy(asyncRegistration = Uninitialized) } + { copy(isLoading = true) }, + { copy(isLoading = false) } ) .assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true)) .finish() @@ -175,8 +175,8 @@ class OnboardingViewModelTest { test .assertStatesChanges( initialState, - { copy(asyncRegistration = Loading()) }, - { copy(asyncRegistration = Uninitialized) } + { copy(isLoading = true) }, + { copy(isLoading = false) } ) .assertNoEvents() .finish() @@ -193,9 +193,8 @@ class OnboardingViewModelTest { test .assertStatesChanges( initialState, - { copy(asyncRegistration = Loading()) }, - { copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) }, - { copy(asyncLoginAction = Success(Unit), asyncRegistration = Uninitialized) } + { copy(isLoading = true) }, + { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) } ) .assertEvents(OnboardingViewEvents.OnAccountCreated) .finish() @@ -211,9 +210,8 @@ class OnboardingViewModelTest { test .assertStatesChanges( initialState, - { copy(asyncRegistration = Loading()) }, - { copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) }, - { copy(asyncRegistration = Uninitialized) } + { copy(isLoading = true) }, + { copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) } ) .assertEvents(OnboardingViewEvents.OnAccountCreated) .finish()