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
|
val safeLoginWizard = loginWizard
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
if (safeLoginWizard == null) {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
_viewEvents.post(OnboardingViewEvents.Failure(Throwable("Bad configuration")))
|
||||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setState {
|
setState { copy(isLoading = true) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
safeLoginWizard.loginWithToken(action.loginToken)
|
val result = safeLoginWizard.loginWithToken(action.loginToken)
|
||||||
|
onSessionCreated(result, isAccountCreated = false)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
?.let { onSessionCreated(it, isAccountCreated = false) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +259,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
private fun handleRegisterAction(action: RegisterAction) {
|
private fun handleRegisterAction(action: RegisterAction) {
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
if (action.hasLoadingState()) {
|
if (action.hasLoadingState()) {
|
||||||
setState { copy(asyncRegistration = Loading()) }
|
setState { copy(isLoading = true) }
|
||||||
}
|
}
|
||||||
runCatching { registrationActionHandler.handleRegisterAction(registrationWizard, action) }
|
runCatching { registrationActionHandler.handleRegisterAction(registrationWizard, action) }
|
||||||
.fold(
|
.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 -> {
|
OnboardingAction.ResetLogin -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
authenticationService.cancelPendingLoginOrRegistration()
|
authenticationService.cancelPendingLoginOrRegistration()
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized,
|
|
||||||
asyncRegistration = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnboardingAction.ResetResetPassword -> {
|
OnboardingAction.ResetResetPassword -> {
|
||||||
|
@ -515,11 +498,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||||
setState {
|
setState { copy(isLoading = true) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
val data = try {
|
val data = try {
|
||||||
|
@ -546,11 +525,7 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onWellKnownError() {
|
private fun onWellKnownError() {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(Exception(stringProvider.getString(R.string.autodiscover_well_known_error))))
|
_viewEvents.post(OnboardingViewEvents.Failure(Exception(stringProvider.getString(R.string.autodiscover_well_known_error))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,18 +562,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
is Failure.UnrecognizedCertificateFailure -> {
|
is Failure.UnrecognizedCertificateFailure -> {
|
||||||
// Display this error in a dialog
|
// Display this error in a dialog
|
||||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Uninitialized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -607,37 +574,22 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
val safeLoginWizard = loginWizard
|
val safeLoginWizard = loginWizard
|
||||||
|
|
||||||
if (safeLoginWizard == null) {
|
if (safeLoginWizard == null) {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
_viewEvents.post(OnboardingViewEvents.Failure(Throwable("Bad configuration")))
|
||||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
setState {
|
setState { copy(isLoading = true) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Loading()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
safeLoginWizard.login(
|
val result = safeLoginWizard.login(
|
||||||
action.username,
|
action.username,
|
||||||
action.password,
|
action.password,
|
||||||
action.initialDeviceName
|
action.initialDeviceName
|
||||||
)
|
)
|
||||||
|
reAuthHelper.data = action.password
|
||||||
|
onSessionCreated(result, isAccountCreated = false)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(
|
|
||||||
asyncLoginAction = Fail(failure)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
?.let {
|
|
||||||
reAuthHelper.data = action.password
|
|
||||||
onSessionCreated(it, isAccountCreated = false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -678,12 +630,12 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
true -> {
|
true -> {
|
||||||
val personalizationState = createPersonalizationState(session, state)
|
val personalizationState = createPersonalizationState(session, state)
|
||||||
setState {
|
setState {
|
||||||
copy(asyncLoginAction = Success(Unit), personalizationState = personalizationState)
|
copy(isLoading = false, personalizationState = personalizationState)
|
||||||
}
|
}
|
||||||
_viewEvents.post(OnboardingViewEvents.OnAccountCreated)
|
_viewEvents.post(OnboardingViewEvents.OnAccountCreated)
|
||||||
}
|
}
|
||||||
false -> {
|
false -> {
|
||||||
setState { copy(asyncLoginAction = Success(Unit)) }
|
setState { copy(isLoading = false) }
|
||||||
_viewEvents.post(OnboardingViewEvents.OnAccountSignedIn)
|
_viewEvents.post(OnboardingViewEvents.OnAccountSignedIn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,14 +664,11 @@ class OnboardingViewModel @AssistedInject constructor(
|
||||||
} else {
|
} else {
|
||||||
currentJob = viewModelScope.launch {
|
currentJob = viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials)
|
val result = authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials)
|
||||||
|
onSessionCreated(result, isAccountCreated = false)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
setState {
|
setState { copy(isLoading = false) }
|
||||||
copy(asyncLoginAction = Fail(failure))
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
?.let { onSessionCreated(it, isAccountCreated = false) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,11 +29,9 @@ import im.vector.app.features.login.SignMode
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
data class OnboardingViewState(
|
data class OnboardingViewState(
|
||||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
|
||||||
val asyncHomeServerLoginFlowRequest: Async<Unit> = Uninitialized,
|
val asyncHomeServerLoginFlowRequest: Async<Unit> = Uninitialized,
|
||||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
val asyncResetPassword: Async<Unit> = Uninitialized,
|
||||||
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
||||||
val asyncRegistration: Async<Unit> = Uninitialized,
|
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
|
|
||||||
@PersistState
|
@PersistState
|
||||||
|
@ -73,11 +71,9 @@ data class OnboardingViewState(
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
fun legacyIsLoading(): Boolean {
|
fun legacyIsLoading(): Boolean {
|
||||||
return asyncLoginAction is Loading ||
|
return asyncHomeServerLoginFlowRequest is Loading ||
|
||||||
asyncHomeServerLoginFlowRequest is Loading ||
|
|
||||||
asyncResetPassword is Loading ||
|
asyncResetPassword is Loading ||
|
||||||
asyncResetMailConfirmed is Loading ||
|
asyncResetMailConfirmed is Loading
|
||||||
asyncRegistration is Loading
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@ import androidx.autofill.HintConstants
|
||||||
import androidx.core.text.isDigitsOnly
|
import androidx.core.text.isDigitsOnly
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.hideKeyboard
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
import im.vector.app.core.extensions.hidePassword
|
import im.vector.app.core.extensions.hidePassword
|
||||||
|
@ -74,6 +72,33 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
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()
|
setupSubmitButton()
|
||||||
setupForgottenPasswordButton()
|
setupForgottenPasswordButton()
|
||||||
|
|
||||||
|
@ -274,39 +299,9 @@ class FtueAuthLoginFragment @Inject constructor() : AbstractSSOFtueAuthFragment<
|
||||||
setupSocialLoginButtons(state)
|
setupSocialLoginButtons(state)
|
||||||
setupButtons(state)
|
setupButtons(state)
|
||||||
|
|
||||||
when (state.asyncLoginAction) {
|
if (state.isLoading) {
|
||||||
is Loading -> {
|
// Ensure password is hidden
|
||||||
// Ensure password is hidden
|
views.passwordField.hidePassword()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,8 +128,8 @@ class OnboardingViewModelTest {
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(signMode = SignMode.SignUp) },
|
{ copy(signMode = SignMode.SignUp) },
|
||||||
{ copy(asyncRegistration = Loading()) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(asyncRegistration = Uninitialized) }
|
{ copy(isLoading = false) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -145,8 +145,8 @@ class OnboardingViewModelTest {
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(asyncRegistration = Loading()) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(asyncRegistration = Uninitialized) }
|
{ copy(isLoading = false) }
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
.assertEvents(OnboardingViewEvents.RegistrationFlowResult(ANY_CONTINUING_REGISTRATION_RESULT.flowResult, isRegistrationStarted = true))
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -175,8 +175,8 @@ class OnboardingViewModelTest {
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(asyncRegistration = Loading()) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(asyncRegistration = Uninitialized) }
|
{ copy(isLoading = false) }
|
||||||
)
|
)
|
||||||
.assertNoEvents()
|
.assertNoEvents()
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -193,9 +193,8 @@ class OnboardingViewModelTest {
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(asyncRegistration = Loading()) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) },
|
{ copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) }
|
||||||
{ copy(asyncLoginAction = Success(Unit), asyncRegistration = Uninitialized) }
|
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -211,9 +210,8 @@ class OnboardingViewModelTest {
|
||||||
test
|
test
|
||||||
.assertStatesChanges(
|
.assertStatesChanges(
|
||||||
initialState,
|
initialState,
|
||||||
{ copy(asyncRegistration = Loading()) },
|
{ copy(isLoading = true) },
|
||||||
{ copy(asyncLoginAction = Success(Unit), personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) },
|
{ copy(isLoading = false, personalizationState = A_HOMESERVER_CAPABILITIES.toPersonalisationState()) }
|
||||||
{ copy(asyncRegistration = Uninitialized) }
|
|
||||||
)
|
)
|
||||||
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
.assertEvents(OnboardingViewEvents.OnAccountCreated)
|
||||||
.finish()
|
.finish()
|
||||||
|
|
Loading…
Reference in New Issue