Created
August 12, 2020 13:58
-
-
Save ashley-figueira/342cddf1fda8215bb8860d3245844d41 to your computer and use it in GitHub Desktop.
A BaseViewModel includes a channel which receives intents and a stateFlow which handles the screen states.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
abstract class BaseViewModel<screenState: BaseScreenState, action: BaseAction>(initialState: screenState) : ViewModel() { | |
val intent = Channel<action>(Channel.UNLIMITED) | |
protected val _state = MutableStateFlow(initialState) | |
val state: StateFlow<screenState> get() = _state | |
init { | |
viewModelScope.launch { | |
handleIntents() | |
} | |
} | |
protected abstract suspend fun handleIntents() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ExperimentalCoroutinesApi | |
@AndroidEntryPoint | |
class LoginFragment : BaseFragment() { | |
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | |
super.onViewCreated(view, savedInstanceState) | |
binding.loginButton.clicks() | |
.onEach { vm.intent.send(LoginActions.LoginButtonClicked(binding.emailInput.text?.toString(), binding.passwordInput.text?.toString())) } | |
.launchIn(lifecycleScope) | |
vm.state | |
.onEach { state -> handleState(state) } | |
.launchIn(lifecycleScope) | |
} | |
private fun handleState(state: LoginScreenState) { | |
when (state) { | |
LoginScreenState.Idle -> { /* do nothing */ } | |
LoginScreenState.Success -> { | |
binding.emailInputLayout.error = null | |
binding.passwordInputLayout.error = null | |
(requireActivity() as MainActivity).showBottomNav() | |
navigateTo(LoginFragmentDirections.actionLoginFragmentToMapsFragment()) | |
} | |
LoginScreenState.EmailNeedsToBeVerified -> Toast.makeText(requireContext(), getString(R.string.login_screen_email_verification_needed), Toast.LENGTH_LONG).show() | |
is LoginScreenState.Loading -> { | |
binding.loginButton.setInvisible(state.isLoading) | |
binding.loginProgress.setVisible(state.isLoading) | |
} | |
is LoginScreenState.Failure -> { | |
state.errors.forEach { | |
when (it) { | |
LoginError.EmailEmpty -> binding.emailInputLayout.error = getString(R.string.login_screen_empty_email_error) | |
LoginError.EmailInvalid -> binding.emailInputLayout.error = getString(R.string.login_screen_invalid_email_error, binding.emailInput.text.toString()) | |
LoginError.PasswordEmpty -> binding.passwordInputLayout.error = getString(R.string.login_screen_password_error) | |
LoginError.PasswordInvalid -> binding.passwordInputLayout.error = getString(R.string.login_screen_password_error) | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@ExperimentalCoroutinesApi | |
class LoginViewModel @ViewModelInject constructor( | |
private val loginWithEmailAndPasswordUseCase: LoginWithEmailAndPasswordUseCase, | |
@Assisted private val savedStateHandle: SavedStateHandle | |
) : BaseViewModel<LoginScreenState, LoginActions>(LoginScreenState.Idle) { | |
override suspend fun handleIntents() { | |
intent.consumeAsFlow().collect { | |
when (it) { | |
is LoginActions.LoginButtonClicked -> { | |
_state.value = LoginScreenState.Loading(true) | |
val result = loginWithEmailAndPasswordUseCase(it.email, it.password) | |
_state.value = LoginScreenState.Loading(false) | |
_state.value = when (result) { | |
is LoginResult.Success -> { | |
when { | |
result.isEmailVerified -> LoginScreenState.Success | |
result.isEmailVerified.not() -> LoginScreenState.EmailNeedsToBeVerified | |
else -> LoginScreenState.Failure(listOf(LoginError.Unknown)) | |
} | |
} | |
is LoginResult.Failure -> LoginScreenState.Failure(result.errors) | |
} | |
} | |
} | |
} | |
} | |
} | |
sealed class LoginActions : BaseAction { | |
data class LoginButtonClicked(val email: String?, val password: String?) : LoginActions() | |
} | |
sealed class LoginScreenState : BaseScreenState { | |
data class Loading(val isLoading: Boolean) : LoginScreenState() | |
data class Failure(val errors: List<LoginError>) : LoginScreenState() | |
object EmailNeedsToBeVerified : LoginScreenState() | |
object Success : LoginScreenState() | |
object Idle : LoginScreenState() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment