replacing async login/register state with separate failure view event and shared isLoading
This commit is contained in:
parent
e3df9c4cef
commit
2227df479c
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,11 +29,9 @@ import im.vector.app.features.login.SignMode
|
|||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
data class OnboardingViewState(
|
||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val asyncHomeServerLoginFlowRequest: Async<Unit> = Uninitialized,
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
||||
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
||||
val asyncRegistration: Async<Unit> = 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue