Skip to content

Instantly share code, notes, and snippets.

@luck-alex13
Last active January 28, 2024 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luck-alex13/78c4b01e5b7b5472dfe17aaa8fd07d97 to your computer and use it in GitHub Desktop.
Save luck-alex13/78c4b01e5b7b5472dfe17aaa8fd07d97 to your computer and use it in GitHub Desktop.
MutableStateFlow with Fragment and ViewModel
@AndroidEntryPoint
class LoginFragment : BaseBindingFragment<LoginFragmentBinding>() {
companion object {
fun screen() = FragmentScreen { LoginFragment() }
}
private val viewModel: LoginViewModel by viewModels()
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LoginFragmentBinding =
LoginFragmentBinding::inflate
private val phoneMask = MaskImpl.createTerminated(PredefinedSlots.RUS_PHONE_NUMBER)
private val formatWatcher: FormatWatcher = MaskFormatWatcher(phoneMask)
override fun setupUI(binding: LoginFragmentBinding) {
binding.apply {
formatWatcher.installOnAndFill(phoneEditText)
phoneEditText.doAfterTextChanged {
phoneEditText.background = drawableCompat(R.drawable.edit_background)
}
passwordField.doAfterTextChanged {
passwordField.background = drawableCompat(R.drawable.edit_background)
}
phoneEditText.setOnEditorActionListener { v, actionId, event ->
return@setOnEditorActionListener when (actionId) {
EditorInfo.IME_ACTION_DONE -> {
hideKeyboard()
true
}
else -> false
}
}
loginButton.isClickable = true
loginButton.setOnClickListener {
viewModel.loginClicked(
formatWatcher.mask.filled(),
formatWatcher.mask.toUnformattedString(),
passwordField.text.toString(),
)
}
registerButton.setOnClickListener {
viewModel.registerClicked()
}
remeberPasswordsButton.setOnClickListener {
viewModel.navigateTo(PassChangeFragment.newInstance())
}
}
}
override fun bindViewModelObservers() {
Log.d(TAG, "bindViewModelObservers:")
lifecycleScope.launch {
viewModel.viewStates()
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.distinctUntilChanged()
.collect {
Log.d(TAG, "viewStates: ${it?.javaClass?.name}")
when (it) {
is ProgressState -> {
showProgress(it.showProgress)
}
is ValidationErrorState -> {
showProgress(false)
handleError(it)
}
is SuccessState<*> -> {
showProgress(false)
}
is ErrorState -> {
showProgress(false)
}
}
}
}
lifecycleScope.launch {
viewModel.oneTimeActions()
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.distinctUntilChanged()
.collect {
Log.d(TAG, "oneTimeActions: ${it?.javaClass?.name}")
when (it) {
is ValidationErrorState -> {
showDefaultDialog(
title = getString(R.string.error_dialog_title),
message = it.errors.toMessage(),
case = InfoDialog.UseCase.Error
)
}
is ErrorState -> {
showErrorForState(it)
}
}
}
}
}
private fun handleError(errorState: ValidationErrorState) {
binding?.apply {
errorState.errors.entries.forEach {
when (it.key) {
ValidationError.IncorrectPhone -> {
phoneEditText.background = drawableCompat(R.drawable.edit_error_background)
}
ValidationError.IncorrectPass -> {
passwordField.background = drawableCompat(R.drawable.edit_error_background)
}
else -> {
}
}
}
}
}
override fun showProgress(show: Boolean) {
binding?.progressView?.isVisible = show
}
}
@HiltViewModel
class LoginViewModel @Inject constructor(
private val userRepository: UserRepository,
router: Router,
application: Application
) : BaseFlowViewModel(router, application) {
private val _viewStates: MutableStateFlow<ViewModelState?> = MutableStateFlow(null)
fun viewStates(): StateFlow<ViewModelState?> = _viewStates
protected var viewState: ViewModelState
get() = _viewStates.value
?: throw UninitializedPropertyAccessException("\"viewState\" was queried before being initialized")
set(value) {
_viewStates.value = value
}
private val _oneTimeActions: MutableStateFlow<ViewModelState?> = MutableStateFlow(null)
fun oneTimeActions(): StateFlow<ViewModelState?> = _oneTimeActions
protected var oneTimeAction: ViewModelState
get() = _oneTimeActions.value
?: throw UninitializedPropertyAccessException("\"viewState\" was queried before being initialized")
set(value) {
_oneTimeActions.value = value
}
fun loginClicked(isFilled: Boolean, phone: String, pass: String) {
val errorsMap = hashMapOf<ValidationError, String>()
if (pass.isNullOrBlank()) {
errorsMap[ValidationError.IncorrectPass] = getString(R.string.empty_password)
}
if (!isFilled) {
errorsMap[ValidationError.IncorrectPhone] = getString(R.string.incorrect_phone)
}
viewModelScope.launch(Dispatchers.IO) {
if (errorsMap.isEmpty()) {
viewState = ProgressState(true)
try {
val res = userRepository.login(phone.filter { it.isDigit() }, pass)//suspend fun
viewState = SuccessState(res)
navigateToAndClearHistory(MainHostFragment())
} catch (ex: Exception) {
viewState = ErrorState(dialogError = makeDialogError(ex))
}
} else {
viewState = ValidationErrorState(errorsMap)//стейт ошибки
oneTimeAction = ValidationErrorState(errorsMap)//показ диалога
}
}
}
fun registerClicked() {
navigateTo(FirstStepFragment())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment