Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-09 15:42:32 +05:00
parent abd131fa7c
commit f532fe1ea1
9 changed files with 122 additions and 90 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.providers.entity
import com.tangem.features.onramp.paymentmethod.entity.PaymentMethodUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.serialization.Serializable
@Serializable
@ -9,6 +10,6 @@ internal sealed interface ProviderListBottomSheetConfig {
@Serializable
data class PaymentMethods(
val selectedMethodId: String,
val paymentMethodsUM: List<PaymentMethodUM>,
val paymentMethodsUM: ImmutableList<PaymentMethodUM>,
) : ProviderListBottomSheetConfig
}

View file

@ -1,8 +1,16 @@
package com.tangem.features.onramp.providers.entity
import com.tangem.domain.onramp.model.OnrampPaymentMethod
import kotlinx.collections.immutable.ImmutableList
internal data class ProviderListUM(
val paymentMethod: ProviderListPaymentMethodUM,
internal data class SelectPaymentAndProviderUM(
val paymentMethods: ImmutableList<OnrampPaymentMethod>,
val selectedPaymentMethod: SelectProviderUM,
val isPaymentMethodClickEnabled: Boolean,
val onPaymentMethodClick: () -> Unit,
)
internal data class SelectProviderUM(
val paymentMethod: OnrampPaymentMethod,
val providers: ImmutableList<ProviderListItemUM>,
)

View file

@ -29,7 +29,9 @@ import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.StringsSigns.MINUS
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
@ -41,24 +43,41 @@ import javax.inject.Inject
internal class SelectProviderModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventHandler: AnalyticsEventHandler,
private val getSelectedPaymentMethodsUseCase: GetOnrampPaymentMethodsUseCase,
private val getOnrampPaymentMethodsUseCase: GetOnrampPaymentMethodsUseCase,
private val getOnrampSelectedPaymentMethodUseCase: GetOnrampSelectedPaymentMethodUseCase,
private val getOnrampProviderWithQuoteUseCase: GetOnrampProviderWithQuoteUseCase,
private val saveSelectedPaymentMethod: OnrampSaveSelectedPaymentMethod,
paramsContainer: ParamsContainer,
) : Model() {
val state: StateFlow<ProviderListUM> get() = _state.asStateFlow()
val state: StateFlow<SelectPaymentAndProviderUM> get() = _state.asStateFlow()
val bottomSheetNavigation: SlotNavigation<ProviderListBottomSheetConfig> = SlotNavigation()
private val params: SelectProviderComponent.Params = paramsContainer.require()
private val _state = MutableStateFlow(getInitialState())
init {
analyticsEventHandler.send(OnrampAnalyticsEvent.ProvidersScreenOpened)
getPaymentMethods()
getProviders(params.selectedPaymentMethod)
subscribeToPaymentMethodUpdates()
}
private fun getPaymentMethods() {
modelScope.launch {
val methods = getOnrampPaymentMethodsUseCase().fold(
ifLeft = { error ->
sendOnrampErrorEvent(error)
emptySet()
},
ifRight = { it },
)
_state.value = state.value.copy(
paymentMethods = methods.toPersistentList(),
isPaymentMethodClickEnabled = methods.isNotEmpty(),
)
}
}
private fun subscribeToPaymentMethodUpdates() {
getOnrampSelectedPaymentMethodUseCase.invoke()
.onEach { maybePaymentMethod ->
@ -74,11 +93,11 @@ internal class SelectProviderModel @Inject constructor(
modelScope.launch {
getOnrampProviderWithQuoteUseCase.invoke(paymentMethod)
.onRight { quotes ->
val hasQuotesData = quotes.any { it is OnrampProviderWithQuote.Data }
_state.update { state ->
state.copy(
paymentMethod = state.paymentMethod.copy(enabled = hasQuotesData),
providers = quotes.toProvidersListItems(),
selectedPaymentMethod = state.selectedPaymentMethod.copy(
providers = quotes.toProvidersListItems(),
),
)
}
}
@ -89,58 +108,47 @@ internal class SelectProviderModel @Inject constructor(
}
}
private fun getInitialState(): ProviderListUM {
return ProviderListUM(
paymentMethod = ProviderListPaymentMethodUM(
id = params.selectedPaymentMethod.id,
name = params.selectedPaymentMethod.name,
imageUrl = params.selectedPaymentMethod.imageUrl,
enabled = false,
onClick = ::openPaymentMethods,
private fun getInitialState(): SelectPaymentAndProviderUM {
return SelectPaymentAndProviderUM(
paymentMethods = persistentListOf(params.selectedPaymentMethod),
isPaymentMethodClickEnabled = false,
onPaymentMethodClick = ::openPaymentMethods,
selectedPaymentMethod = SelectProviderUM(
paymentMethod = params.selectedPaymentMethod,
providers = emptyList<ProviderListItemUM>().toImmutableList(),
),
providers = emptyList<ProviderListItemUM>().toImmutableList(),
)
}
private fun openPaymentMethods() {
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened)
modelScope.launch {
val methods = getSelectedPaymentMethodsUseCase().fold(
ifLeft = { error ->
sendOnrampErrorEvent(error)
emptySet()
},
ifRight = { it },
)
bottomSheetNavigation.activate(
ProviderListBottomSheetConfig.PaymentMethods(
selectedMethodId = _state.value.paymentMethod.id,
paymentMethodsUM = methods.toPaymentUMList(),
),
)
}
bottomSheetNavigation.activate(
ProviderListBottomSheetConfig.PaymentMethods(
selectedMethodId = state.value.selectedPaymentMethod.paymentMethod.id,
paymentMethodsUM = state.value.paymentMethods.toPaymentUMList(),
),
)
}
private fun Set<OnrampPaymentMethod>.toPaymentUMList(): List<PaymentMethodUM> = map { method ->
private fun ImmutableList<OnrampPaymentMethod>.toPaymentUMList(): ImmutableList<PaymentMethodUM> = map { method ->
PaymentMethodUM(
id = method.id,
imageUrl = method.imageUrl,
name = method.name,
onSelect = { onPaymentMethodSelected(method) },
)
}
}.toPersistentList()
private fun onPaymentMethodSelected(paymentMethod: OnrampPaymentMethod) {
analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen(paymentMethod = paymentMethod.name))
modelScope.launch {
saveSelectedPaymentMethod.invoke(paymentMethod)
_state.update { state ->
val paymentMethodUM = state.paymentMethod.copy(
id = paymentMethod.id,
name = paymentMethod.name,
imageUrl = paymentMethod.imageUrl,
state.copy(
selectedPaymentMethod = state.selectedPaymentMethod.copy(
paymentMethod = paymentMethod,
),
)
state.copy(paymentMethod = paymentMethodUM)
}
bottomSheetNavigation.dismiss()
}
@ -237,7 +245,7 @@ internal class SelectProviderModel @Inject constructor(
}
private fun sendOnrampErrorEvent(error: OnrampError) {
val selectedProvider = state.value.providers.firstOrNull {
val selectedProvider = state.value.selectedPaymentMethod.providers.firstOrNull {
it.providerId == params.selectedProviderId
}
analyticsEventHandler.sendOnrampErrorEvent(

View file

@ -2,21 +2,26 @@ package com.tangem.features.onramp.providers.model.previewData
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.onramp.model.OnrampPaymentMethod
import com.tangem.domain.onramp.model.PaymentMethodType
import com.tangem.features.onramp.providers.entity.ProviderListItemUM
import com.tangem.features.onramp.providers.entity.ProviderListPaymentMethodUM
import com.tangem.features.onramp.providers.entity.ProviderListUM
import com.tangem.features.onramp.providers.entity.SelectPaymentAndProviderUM
import com.tangem.features.onramp.providers.entity.SelectProviderUM
import kotlinx.collections.immutable.persistentListOf
internal object SelectProviderPreviewData {
val state = ProviderListUM(
paymentMethod = ProviderListPaymentMethodUM(
private val paymentMethods = persistentListOf(
OnrampPaymentMethod(
id = "card",
name = "Card",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
onClick = {},
enabled = false,
type = PaymentMethodType.CARD,
),
)
private val providerListUM = SelectProviderUM(
paymentMethod = paymentMethods.first(),
providers = persistentListOf(
ProviderListItemUM.Available(
providerId = "mercuryo",
@ -46,4 +51,11 @@ internal object SelectProviderPreviewData {
),
),
)
val state = SelectPaymentAndProviderUM(
paymentMethods = paymentMethods,
selectedPaymentMethod = providerListUM,
isPaymentMethodClickEnabled = false,
onPaymentMethodClick = {},
)
}

View file

@ -32,11 +32,11 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.onramp.model.OnrampPaymentMethod
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.paymentmethod.ui.PaymentMethodIcon
import com.tangem.features.onramp.providers.entity.ProviderListItemUM
import com.tangem.features.onramp.providers.entity.ProviderListPaymentMethodUM
import com.tangem.features.onramp.providers.entity.ProviderListUM
import com.tangem.features.onramp.providers.entity.SelectPaymentAndProviderUM
import com.tangem.features.onramp.providers.model.previewData.SelectProviderPreviewData
import com.tangem.features.onramp.utils.selectedBorder
@ -50,7 +50,7 @@ internal fun SelectProviderBottomSheet(config: TangemBottomSheetConfig, content:
}
@Composable
internal fun SelectProviderBottomSheetContent(state: ProviderListUM, modifier: Modifier = Modifier) {
internal fun SelectProviderBottomSheetContent(state: SelectPaymentAndProviderUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.primary)
@ -65,16 +65,23 @@ internal fun SelectProviderBottomSheetContent(state: ProviderListUM, modifier: M
)
PaymentMethodBlock(
modifier = Modifier.padding(top = TangemTheme.dimens.spacing20, bottom = TangemTheme.dimens.spacing16),
state = state.paymentMethod,
state = state.selectedPaymentMethod.paymentMethod,
isClickEnabled = state.isPaymentMethodClickEnabled,
onPaymentMethodClick = state.onPaymentMethodClick,
)
state.providers.fastForEach { provider ->
state.selectedPaymentMethod.providers.fastForEach { provider ->
key(provider.providerId) { ProviderItem(state = provider) }
}
}
}
@Composable
private fun PaymentMethodBlock(state: ProviderListPaymentMethodUM, modifier: Modifier = Modifier) {
private fun PaymentMethodBlock(
state: OnrampPaymentMethod,
isClickEnabled: Boolean,
onPaymentMethodClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
@ -83,7 +90,7 @@ private fun PaymentMethodBlock(state: ProviderListPaymentMethodUM, modifier: Mod
color = TangemTheme.colors.stroke.secondary,
shape = RoundedCornerShape(TangemTheme.dimens.radius16),
)
.clickable(enabled = state.enabled, onClick = state.onClick)
.clickable(enabled = isClickEnabled, onClick = onPaymentMethodClick)
.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
@ -247,7 +254,7 @@ private fun UnavailableProviderItem(
@Composable
private fun SelectProviderBottomSheetContent_Preview(
@PreviewParameter(SelectProviderBottomSheetContentPreviewProvider::class)
data: ProviderListUM,
data: SelectPaymentAndProviderUM,
) {
TangemThemePreview {
SelectProviderBottomSheetContent(data)
@ -255,8 +262,8 @@ private fun SelectProviderBottomSheetContent_Preview(
}
private class SelectProviderBottomSheetContentPreviewProvider :
PreviewParameterProvider<ProviderListUM> {
override val values: Sequence<ProviderListUM>
PreviewParameterProvider<SelectPaymentAndProviderUM> {
override val values: Sequence<SelectPaymentAndProviderUM>
get() = sequenceOf(
SelectProviderPreviewData.state,
)