diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/ComponentFieldViewState.kt b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/ComponentFieldViewState.kt index 9bc70e4037..96c06195e1 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/ComponentFieldViewState.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/internal/ui/model/ComponentFieldViewState.kt @@ -14,5 +14,5 @@ import androidx.annotation.StringRes @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class ComponentFieldViewState( val value: T, - @StringRes val errorMessageId: Int? = null + @StringRes val errorMessageId: Int? = null, ) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt index 605880d366..ce42da50e5 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/DefaultMBWayDelegate.kt @@ -58,7 +58,10 @@ internal class DefaultMBWayDelegate( private val validationRegistry: FieldValidatorRegistry, ) : MBWayDelegate { - private var state = MutableStateFlow(MBWayDelegateState()) + private var state = MutableStateFlow(MBWayDelegateState( + countries = getSupportedCountries(), + initiallySelectedCountry = getInitiallySelectedCountry() + )) private val _componentStateFlow = MutableStateFlow(createComponentState()) override val componentStateFlow: Flow = _componentStateFlow @@ -139,7 +142,6 @@ internal class DefaultMBWayDelegate( MBWayFieldId.COUNTRY_CODE -> state.value.countryCodeFieldState.value MBWayFieldId.LOCAL_PHONE_NUMBER -> state.value.localPhoneNumberFieldState.value } - // TODO: Update only the fields which are not validated yet. updateField(fieldId, value = value, isValidationErrorCheckForced = true) } } @@ -220,10 +222,10 @@ internal class DefaultMBWayDelegate( _componentStateFlow.tryEmit(componentState) } - override fun getSupportedCountries(): List = + private fun getSupportedCountries(): List = CountryUtils.getLocalizedCountries(componentParams.shopperLocale, SUPPORTED_COUNTRIES) - override fun getInitiallySelectedCountry(): CountryModel? { + private fun getInitiallySelectedCountry(): CountryModel? { val countries = getSupportedCountries() return countries.firstOrNull { it.isoCode == ISO_CODE_PORTUGAL } ?: countries.firstOrNull() } diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt index 26f9df275f..34a7e79cee 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/MBWayDelegate.kt @@ -15,7 +15,6 @@ import com.adyen.checkout.mbway.internal.ui.model.MBWayViewState import com.adyen.checkout.ui.core.internal.ui.ButtonDelegate import com.adyen.checkout.ui.core.internal.ui.UIStateDelegate import com.adyen.checkout.ui.core.internal.ui.ViewProvidingDelegate -import com.adyen.checkout.ui.core.internal.ui.model.CountryModel import kotlinx.coroutines.flow.Flow internal interface MBWayDelegate : @@ -28,10 +27,6 @@ internal interface MBWayDelegate : val componentStateFlow: Flow - fun getSupportedCountries(): List - - fun getInitiallySelectedCountry(): CountryModel? - fun onFieldValueChanged(fieldId: MBWayFieldId, value: String) fun onFieldFocusChanged(fieldId: MBWayFieldId, hasFocus: Boolean) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayDelegateState.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayDelegateState.kt index 37b80da3c3..1e3efedf25 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayDelegateState.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayDelegateState.kt @@ -11,27 +11,31 @@ package com.adyen.checkout.mbway.internal.ui.model import com.adyen.checkout.components.core.internal.ui.model.ComponentFieldDelegateState import com.adyen.checkout.components.core.internal.ui.model.ComponentFieldViewState import com.adyen.checkout.components.core.internal.ui.model.Validation +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel internal data class MBWayDelegateState( - val countryCodeFieldState: ComponentFieldDelegateState = ComponentFieldDelegateState( - value = "", - validation = Validation.Valid, - ), + val countries: List, + val initiallySelectedCountry: CountryModel?, + val countryCodeFieldState: ComponentFieldDelegateState = ComponentFieldDelegateState(value = ""), val localPhoneNumberFieldState: ComponentFieldDelegateState = ComponentFieldDelegateState(value = ""), ) { val isValid: Boolean - get() = - countryCodeFieldState.validation?.isValid() == true && - localPhoneNumberFieldState.validation?.isValid() == true + get() = countryCodeFieldState.validation?.isValid() == true && + localPhoneNumberFieldState.validation?.isValid() == true } internal fun MBWayDelegateState.toViewState() = MBWayViewState( - phoneNumberFieldState = ComponentFieldViewState( - value = this.localPhoneNumberFieldState.value, - errorMessageId = this.localPhoneNumberFieldState.takeIf { fieldState -> - fieldState.shouldShowValidationError() - }?.validation.let { it as? Validation.Invalid }?.reason, - ), + countries = this.countries, + initiallySelectedCountry = initiallySelectedCountry, + phoneNumberFieldState = with(this.localPhoneNumberFieldState) { + ComponentFieldViewState( + value = value, + errorMessageId = takeIf { fieldState -> + fieldState.shouldShowValidationError() + }?.validation.let { it as? Validation.Invalid }?.reason, + ) + }, ) -internal fun ComponentFieldDelegateState.shouldShowValidationError() = !this.hasFocus || this.isValidationErrorCheckForced +internal fun ComponentFieldDelegateState.shouldShowValidationError() = + !this.hasFocus || this.isValidationErrorCheckForced diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayViewState.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayViewState.kt index 7892f5f75e..6b18aaa284 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayViewState.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/model/MBWayViewState.kt @@ -9,7 +9,10 @@ package com.adyen.checkout.mbway.internal.ui.model import com.adyen.checkout.components.core.internal.ui.model.ComponentFieldViewState +import com.adyen.checkout.ui.core.internal.ui.model.CountryModel internal data class MBWayViewState( + val countries: List, + val initiallySelectedCountry: CountryModel?, val phoneNumberFieldState: ComponentFieldViewState, ) diff --git a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt index 8963044e34..51c9fe6f51 100644 --- a/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt +++ b/mbway/src/main/java/com/adyen/checkout/mbway/internal/ui/view/MbWayView.kt @@ -19,6 +19,7 @@ import com.adyen.checkout.components.core.internal.ui.model.ComponentFieldViewSt import com.adyen.checkout.mbway.databinding.MbwayViewBinding import com.adyen.checkout.mbway.internal.ui.MBWayDelegate import com.adyen.checkout.mbway.internal.ui.model.MBWayFieldId +import com.adyen.checkout.mbway.internal.ui.model.MBWayViewState import com.adyen.checkout.ui.core.internal.ui.ComponentView import com.adyen.checkout.ui.core.internal.ui.CountryAdapter import com.adyen.checkout.ui.core.internal.ui.model.CountryModel @@ -42,6 +43,8 @@ internal class MbWayView @JvmOverloads constructor( private lateinit var delegate: MBWayDelegate + private lateinit var countryAdapter: CountryAdapter + init { orientation = VERTICAL @@ -54,17 +57,23 @@ internal class MbWayView @JvmOverloads constructor( this.delegate = delegate this.localizedContext = localizedContext - observeDelegate(delegate, coroutineScope) - initMobileNumberInput() initCountryInput() - } + observeDelegate(delegate, coroutineScope) + } - private fun observeDelegate(delegate: MBWayDelegate, coroutineScope: CoroutineScope) { - delegate.viewStateFlow - .onEach { updateMobileNumberInput(it.phoneNumberFieldState) } - .launchIn(coroutineScope) + private fun initCountryInput() { + countryAdapter = CountryAdapter(context, localizedContext) + binding.autoCompleteTextViewCountry.apply { + // disable editing and hide cursor + inputType = 0 + setAdapter(countryAdapter) + setOnItemClickListener { _, _, position, _ -> + val country = countryAdapter.getItem(position) + onCountrySelected(country) + } + } } private fun initMobileNumberInput() { @@ -76,6 +85,17 @@ internal class MbWayView @JvmOverloads constructor( } } + private fun observeDelegate(delegate: MBWayDelegate, coroutineScope: CoroutineScope) { + delegate.viewStateFlow + .onEach { viewStateUpdated(it) } + .launchIn(coroutineScope) + } + + private fun viewStateUpdated(mbWayViewState: MBWayViewState) { + updateMobileNumberInput(mbWayViewState.phoneNumberFieldState) + updateCountryInput(mbWayViewState.countries, mbWayViewState.initiallySelectedCountry) + } + private fun updateMobileNumberInput(phoneNumberFieldState: ComponentFieldViewState) { phoneNumberFieldState.errorMessageId?.let { errorMessageId -> binding.textInputLayoutMobileNumber.showError( @@ -86,23 +106,15 @@ internal class MbWayView @JvmOverloads constructor( } } - private fun initCountryInput() { - val countries = delegate.getSupportedCountries() - val adapter = CountryAdapter(context, localizedContext) - adapter.setItems(countries) - binding.autoCompleteTextViewCountry.apply { - // disable editing and hide cursor - inputType = 0 - setAdapter(adapter) - setOnItemClickListener { _, _, position, _ -> - val country = adapter.getItem(position) - onCountrySelected(country) + private fun updateCountryInput(countries: List, initiallySelectedCountry: CountryModel?) { + if (countryAdapter.isEmpty) { + initiallySelectedCountry?.let { + binding.autoCompleteTextViewCountry.setText(initiallySelectedCountry.toShortString()) + onCountrySelected(initiallySelectedCountry) } } - delegate.getInitiallySelectedCountry()?.let { - binding.autoCompleteTextViewCountry.setText(it.toShortString()) - onCountrySelected(it) - } + + countryAdapter.setItems(countries) } override fun highlightValidationErrors() {