Migrate Login Navigation view model to regular ViewEvents

This commit is contained in:
Benoit Marty 2020-03-02 19:11:52 +01:00
parent c9bc6f4a9e
commit f74b1e6c2e
19 changed files with 102 additions and 158 deletions

View File

@ -30,11 +30,10 @@ import im.vector.riotx.features.home.HomeSharedActionViewModel
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
import im.vector.riotx.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import im.vector.riotx.features.login.LoginSharedActionViewModel
import im.vector.riotx.features.userdirectory.UserDirectorySharedActionViewModel
import im.vector.riotx.features.reactions.EmojiChooserViewModel
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel
import im.vector.riotx.features.roomprofile.RoomProfileSharedActionViewModel
import im.vector.riotx.features.userdirectory.UserDirectorySharedActionViewModel
import im.vector.riotx.features.workers.signout.SignOutViewModel
@Module
@ -110,11 +109,6 @@ interface ViewModelModule {
@ViewModelKey(RoomDirectorySharedActionViewModel::class)
fun bindRoomDirectorySharedActionViewModel(viewModel: RoomDirectorySharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(LoginSharedActionViewModel::class)
fun bindLoginSharedActionViewModel(viewModel: LoginSharedActionViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(RoomDetailSharedActionViewModel::class)

View File

@ -38,7 +38,6 @@ import javax.net.ssl.HttpsURLConnection
abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
protected val loginViewModel: LoginViewModel by activityViewModel()
protected lateinit var loginSharedActionViewModel: LoginSharedActionViewModel
private var isResetPasswordStarted = false
@ -57,8 +56,6 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loginSharedActionViewModel = activityViewModelProvider.get(LoginSharedActionViewModel::class.java)
loginViewModel.observeViewEvents {
handleLoginViewEvents(it)
}

View File

@ -58,4 +58,6 @@ sealed class LoginAction : VectorViewModelAction {
// For the soft logout case
data class SetupSsoForSessionRecovery(val homeServerUrl: String, val deviceId: String) : LoginAction()
data class PostViewEvent(val viewEvent: LoginViewEvents) : LoginAction()
}

View File

@ -55,7 +55,6 @@ import javax.inject.Inject
open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
private val loginViewModel: LoginViewModel by viewModel()
private lateinit var loginSharedActionViewModel: LoginSharedActionViewModel
@Inject lateinit var loginViewModelFactory: LoginViewModel.Factory
@ -99,14 +98,6 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
loginViewModel.handle(LoginAction.InitWith(loginConfig))
}
loginSharedActionViewModel = viewModelProvider.get(LoginSharedActionViewModel::class.java)
loginSharedActionViewModel
.observe()
.subscribe {
handleLoginNavigation(it)
}
.disposeOnDestroy()
loginViewModel
.subscribe(this) {
updateWithState(it)
@ -125,63 +116,9 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
addFragment(R.id.loginFragmentContainer, LoginSplashFragment::class.java)
}
private fun handleLoginNavigation(loginNavigation: LoginNavigation) {
when (loginNavigation) {
is LoginNavigation.OpenServerSelection ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginServerSelectionFragment::class.java,
option = { ft ->
findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO Disabled because it provokes a flickering
// ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
})
is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone()
is LoginNavigation.OnSignModeSelected -> onSignModeSelected()
is LoginNavigation.OnLoginFlowRetrieved ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginSignUpSignInSelectionFragment::class.java,
option = commonOption)
is LoginNavigation.OnWebLoginError -> onWebLoginError(loginNavigation)
is LoginNavigation.OnForgetPasswordClicked ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordFragment::class.java,
option = commonOption)
is LoginNavigation.OnResetPasswordSendThreePidDone -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordMailConfirmationFragment::class.java,
option = commonOption)
}
is LoginNavigation.OnResetPasswordMailConfirmationSuccess -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordSuccessFragment::class.java,
option = commonOption)
}
is LoginNavigation.OnResetPasswordMailConfirmationSuccessDone -> {
// Go back to the login fragment
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
}
is LoginNavigation.OnSendEmailSuccess ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginWaitForEmailFragment::class.java,
LoginWaitForEmailFragmentArgument(loginNavigation.email),
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
option = commonOption)
is LoginNavigation.OnSendMsisdnSuccess ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginGenericTextInputFormFragment::class.java,
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginNavigation.msisdn),
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
option = commonOption)
}.exhaustive
}
private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) {
when (loginViewEvents) {
is LoginViewEvents.RegistrationFlowResult -> {
is LoginViewEvents.RegistrationFlowResult -> {
// Check that all flows are supported by the application
if (loginViewEvents.flowResult.missingStages.any { !it.isSupported() }) {
// Display a popup to propose use web fallback
@ -202,15 +139,65 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
}
}
}
is LoginViewEvents.OutdatedHomeserver ->
is LoginViewEvents.OutdatedHomeserver ->
AlertDialog.Builder(this)
.setTitle(R.string.login_error_outdated_homeserver_title)
.setMessage(R.string.login_error_outdated_homeserver_content)
.setPositiveButton(R.string.ok, null)
.show()
is LoginViewEvents.Failure ->
is LoginViewEvents.Failure ->
// This is handled by the Fragments
Unit
is LoginViewEvents.OpenServerSelection ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginServerSelectionFragment::class.java,
option = { ft ->
findViewById<View?>(R.id.loginSplashLogo)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashTitle)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
findViewById<View?>(R.id.loginSplashSubmit)?.let { ft.addSharedElement(it, ViewCompat.getTransitionName(it) ?: "") }
// TODO Disabled because it provokes a flickering
// ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
})
is LoginViewEvents.OnServerSelectionDone -> onServerSelectionDone()
is LoginViewEvents.OnSignModeSelected -> onSignModeSelected()
is LoginViewEvents.OnLoginFlowRetrieved ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginSignUpSignInSelectionFragment::class.java,
option = commonOption)
is LoginViewEvents.OnWebLoginError -> onWebLoginError(loginViewEvents)
is LoginViewEvents.OnForgetPasswordClicked ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordFragment::class.java,
option = commonOption)
is LoginViewEvents.OnResetPasswordSendThreePidDone -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordMailConfirmationFragment::class.java,
option = commonOption)
}
is LoginViewEvents.OnResetPasswordMailConfirmationSuccess -> {
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginResetPasswordSuccessFragment::class.java,
option = commonOption)
}
is LoginViewEvents.OnResetPasswordMailConfirmationSuccessDone -> {
// Go back to the login fragment
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
}
is LoginViewEvents.OnSendEmailSuccess ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginWaitForEmailFragment::class.java,
LoginWaitForEmailFragmentArgument(loginViewEvents.email),
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
option = commonOption)
is LoginViewEvents.OnSendMsisdnSuccess ->
addFragmentToBackstack(R.id.loginFragmentContainer,
LoginGenericTextInputFormFragment::class.java,
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginViewEvents.msisdn),
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
option = commonOption)
}
}
@ -229,7 +216,7 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
loginLoading.isVisible = loginViewState.isLoading()
}
private fun onWebLoginError(onWebLoginError: LoginNavigation.OnWebLoginError) {
private fun onWebLoginError(onWebLoginError: LoginViewEvents.OnWebLoginError) {
// Pop the backstack
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)

View File

@ -190,7 +190,7 @@ class LoginFragment @Inject constructor() : AbstractLoginFragment() {
@OnClick(R.id.forgetPasswordButton)
fun forgetPasswordClicked() {
loginSharedActionViewModel.post(LoginNavigation.OnForgetPasswordClicked)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnForgetPasswordClicked))
}
private fun setupPasswordReveal() {

View File

@ -217,7 +217,7 @@ class LoginGenericTextInputFormFragment @Inject constructor() : AbstractLoginFra
TextInputFormFragmentMode.SetEmail -> {
if (throwable.is401()) {
// This is normal use case, we go to the mail waiting screen
loginSharedActionViewModel.post(LoginNavigation.OnSendEmailSuccess(loginViewModel.currentThreePid ?: ""))
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnSendEmailSuccess(loginViewModel.currentThreePid ?: "")))
} else {
loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
}
@ -225,7 +225,7 @@ class LoginGenericTextInputFormFragment @Inject constructor() : AbstractLoginFra
TextInputFormFragmentMode.SetMsisdn -> {
if (throwable.is401()) {
// This is normal use case, we go to the enter code screen
loginSharedActionViewModel.post(LoginNavigation.OnSendMsisdnSuccess(loginViewModel.currentThreePid ?: ""))
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnSendMsisdnSuccess(loginViewModel.currentThreePid ?: "")))
} else {
loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
}

View File

@ -1,36 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.login
import im.vector.riotx.core.platform.VectorSharedAction
// Supported navigation actions for LoginActivity
sealed class LoginNavigation : VectorSharedAction {
object OpenServerSelection : LoginNavigation()
object OnServerSelectionDone : LoginNavigation()
object OnLoginFlowRetrieved : LoginNavigation()
object OnSignModeSelected : LoginNavigation()
object OnForgetPasswordClicked : LoginNavigation()
object OnResetPasswordSendThreePidDone : LoginNavigation()
object OnResetPasswordMailConfirmationSuccess : LoginNavigation()
object OnResetPasswordMailConfirmationSuccessDone : LoginNavigation()
data class OnSendEmailSuccess(val email: String) : LoginNavigation()
data class OnSendMsisdnSuccess(val msisdn: String) : LoginNavigation()
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginNavigation()
}

View File

@ -149,7 +149,7 @@ class LoginResetPasswordFragment @Inject constructor() : AbstractLoginFragment()
resetPasswordEmailTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error)
}
is Success -> {
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSendThreePidDone)
Unit
}
}
}

View File

@ -64,7 +64,7 @@ class LoginResetPasswordMailConfirmationFragment @Inject constructor() : Abstrac
.show()
}
is Success -> {
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordMailConfirmationSuccess)
Unit
}
}
}

View File

@ -29,7 +29,7 @@ class LoginResetPasswordSuccessFragment @Inject constructor() : AbstractLoginFra
@OnClick(R.id.resetPasswordSuccessSubmit)
fun submit() {
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordMailConfirmationSuccessDone)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnResetPasswordMailConfirmationSuccessDone))
}
override fun resetViewModel() {

View File

@ -95,14 +95,13 @@ class LoginServerSelectionFragment @Inject constructor() : AbstractLoginFragment
// Request login flow here
loginViewModel.handle(LoginAction.UpdateHomeServer(getString(R.string.matrix_org_server_url)))
} else {
loginSharedActionViewModel.post(LoginNavigation.OnServerSelectionDone)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnServerSelectionDone))
}
}
@OnClick(R.id.loginServerIKnowMyIdSubmit)
fun loginWithMatrixId() {
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignInWithMatrixId))
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
}
override fun resetViewModel() {
@ -114,7 +113,7 @@ class LoginServerSelectionFragment @Inject constructor() : AbstractLoginFragment
if (state.loginMode != LoginMode.Unknown) {
// LoginFlow for matrix.org has been retrieved
loginSharedActionViewModel.post(LoginNavigation.OnLoginFlowRetrieved)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnLoginFlowRetrieved))
}
}
}

View File

@ -126,7 +126,7 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
if (state.loginMode != LoginMode.Unknown) {
// The home server url is valid
loginSharedActionViewModel.post(LoginNavigation.OnLoginFlowRetrieved)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnLoginFlowRetrieved))
}
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.login
import im.vector.riotx.core.platform.VectorSharedActionViewModel
import javax.inject.Inject
class LoginSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel<LoginNavigation>()

View File

@ -78,7 +78,6 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
@OnClick(R.id.loginSignupSigninSignIn)
fun signIn() {
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignIn))
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
}
override fun resetViewModel() {

View File

@ -29,7 +29,7 @@ class LoginSplashFragment @Inject constructor() : AbstractLoginFragment() {
@OnClick(R.id.loginSplashSubmit)
fun getStarted() {
loginSharedActionViewModel.post(LoginNavigation.OpenServerSelection)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OpenServerSelection))
}
override fun resetViewModel() {

View File

@ -23,10 +23,26 @@ import im.vector.riotx.core.platform.VectorViewEvents
/**
* Transient events for Login
*/
sealed class LoginViewEvents: VectorViewEvents {
sealed class LoginViewEvents : VectorViewEvents {
data class Loading(val message: CharSequence? = null) : LoginViewEvents()
data class Failure(val throwable: Throwable) : LoginViewEvents()
data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : LoginViewEvents()
object OutdatedHomeserver : LoginViewEvents()
// Navigation event
object OpenServerSelection : LoginViewEvents()
object OnServerSelectionDone : LoginViewEvents()
object OnLoginFlowRetrieved : LoginViewEvents()
object OnSignModeSelected : LoginViewEvents()
object OnForgetPasswordClicked : LoginViewEvents()
object OnResetPasswordSendThreePidDone : LoginViewEvents()
object OnResetPasswordMailConfirmationSuccess : LoginViewEvents()
object OnResetPasswordMailConfirmationSuccessDone : LoginViewEvents()
data class OnSendEmailSuccess(val email: String) : LoginViewEvents()
data class OnSendMsisdnSuccess(val msisdn: String) : LoginViewEvents()
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginViewEvents()
}

View File

@ -116,7 +116,8 @@ class LoginViewModel @AssistedInject constructor(
is LoginAction.RegisterAction -> handleRegisterAction(action)
is LoginAction.ResetAction -> handleResetAction(action)
is LoginAction.SetupSsoForSessionRecovery -> handleSetupSsoForSessionRecovery(action)
}
is LoginAction.PostViewEvent -> _viewEvents.post(action.viewEvent)
}.exhaustive
}
private fun handleSetupSsoForSessionRecovery(action: LoginAction.SetupSsoForSessionRecovery) {
@ -328,11 +329,12 @@ class LoginViewModel @AssistedInject constructor(
)
}
if (action.signMode == SignMode.SignUp) {
startRegistrationFlow()
} else if (action.signMode == SignMode.SignIn) {
startAuthenticationFlow()
}
when (action.signMode) {
SignMode.SignUp -> startRegistrationFlow()
SignMode.SignIn -> startAuthenticationFlow()
SignMode.SignInWithMatrixId -> _viewEvents.post(LoginViewEvents.OnSignModeSelected)
SignMode.Unknown -> Unit
}.exhaustive
}
private fun handleUpdateServerType(action: LoginAction.UpdateServerType) {
@ -373,6 +375,8 @@ class LoginViewModel @AssistedInject constructor(
resetPasswordEmail = action.email
)
}
_viewEvents.post(LoginViewEvents.OnResetPasswordSendThreePidDone)
}
override fun onFailure(failure: Throwable) {
@ -413,6 +417,8 @@ class LoginViewModel @AssistedInject constructor(
resetPasswordEmail = null
)
}
_viewEvents.post(LoginViewEvents.OnResetPasswordMailConfirmationSuccess)
}
override fun onFailure(failure: Throwable) {
@ -548,6 +554,8 @@ class LoginViewModel @AssistedInject constructor(
private fun startAuthenticationFlow() {
// Ensure Wizard is ready
loginWizard
_viewEvents.post(LoginViewEvents.OnSignModeSelected)
}
private fun onFlowResponse(flowResult: FlowResult) {

View File

@ -173,7 +173,7 @@ class LoginWebFragment @Inject constructor(
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
super.onReceivedError(view, errorCode, description, failingUrl)
loginSharedActionViewModel.post(LoginNavigation.OnWebLoginError(errorCode, description, failingUrl))
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnWebLoginError(errorCode, description, failingUrl)))
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {

View File

@ -30,7 +30,7 @@ import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.features.login.AbstractLoginFragment
import im.vector.riotx.features.login.LoginAction
import im.vector.riotx.features.login.LoginMode
import im.vector.riotx.features.login.LoginNavigation
import im.vector.riotx.features.login.LoginViewEvents
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import javax.inject.Inject
@ -94,7 +94,7 @@ class SoftLogoutFragment @Inject constructor(
}
override fun signinFallbackSubmit() {
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnSignModeSelected))
}
override fun clearData() {
@ -124,7 +124,7 @@ class SoftLogoutFragment @Inject constructor(
}
override fun forgetPasswordClicked() {
loginSharedActionViewModel.post(LoginNavigation.OnForgetPasswordClicked)
loginViewModel.handle(LoginAction.PostViewEvent(LoginViewEvents.OnForgetPasswordClicked))
}
override fun revealPasswordClicked() {