Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-25 14:14:58 +05:00
parent 44231e988d
commit e72c2c87bc
28 changed files with 426 additions and 105 deletions

View file

@ -72,7 +72,7 @@ internal class OnrampStateFactory(
isValuePasted = false,
onValuePastedTriggerDismiss = {},
),
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading,
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY),
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.features.onramp.main.entity.factory.amount
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
import com.tangem.utils.Provider
@ -56,7 +57,7 @@ internal class OnrampAmountFieldChangeConverter(
return copy(
amountBlockState = amountBlockState.copy(
amountFieldModel = amountFieldModel,
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading,
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(TextReference.EMPTY),
),
)
}

View file

@ -1,7 +1,17 @@
package com.tangem.features.onramp.main.entity.factory.amount
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.onramp.model.OnrampCurrency
import com.tangem.domain.onramp.model.OnrampQuote
import com.tangem.domain.tokens.model.AmountType
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.main.entity.OnrampAmountBlockUM
import com.tangem.features.onramp.main.entity.OnrampAmountSecondaryFieldUM
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
import com.tangem.utils.Provider
@ -35,4 +45,69 @@ internal class OnrampAmountStateFactory(private val currentStateProvider: Provid
),
)
}
fun getAmountSecondaryLoadingState(): OnrampMainComponentUM {
val currentState = currentStateProvider()
if (currentState !is OnrampMainComponentUM.Content) return currentState
val amountState = currentState.amountBlockState
return currentState.copy(
amountBlockState = amountState.copy(secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading),
)
}
fun getAmountSecondaryUpdatedState(quote: OnrampQuote): OnrampMainComponentUM {
val currentState = currentStateProvider()
if (currentState !is OnrampMainComponentUM.Content) return currentState
val amountState = currentState.amountBlockState
return currentState.copy(
amountBlockState = amountState.copy(
secondaryFieldModel = quote.toUiModel(amountState),
),
)
}
private fun OnrampQuote.toUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM {
return when (this) {
is OnrampQuote.Content -> {
val amount = toAmount.format {
crypto(
symbol = amountState.amountFieldModel.cryptoAmount.currencySymbol,
decimals = amountState.amountFieldModel.cryptoAmount.decimals,
)
}
OnrampAmountSecondaryFieldUM.Content(stringReference(amount))
}
is OnrampQuote.Error.AmountTooBigError -> {
val amount = this.amount.value.format {
fiat(
fiatCurrencyCode = amountState.amountFieldModel.fiatAmount.currencySymbol,
fiatCurrencySymbol = amountState.amountFieldModel.fiatAmount.currencySymbol,
)
}
OnrampAmountSecondaryFieldUM.Error(
resourceReference(
R.string.onramp_max_amount_restriction,
wrappedList(amount),
),
)
}
is OnrampQuote.Error.AmountTooSmallError -> {
val amount = this.amount.value.format {
fiat(
fiatCurrencyCode = amountState.amountFieldModel.fiatAmount.currencySymbol,
fiatCurrencySymbol = amountState.amountFieldModel.fiatAmount.currencySymbol,
)
}
OnrampAmountSecondaryFieldUM.Error(
resourceReference(
R.string.onramp_min_amount_restriction,
wrappedList(amount),
),
)
}
}
}
}

View file

@ -5,9 +5,7 @@ import com.arkivanov.decompose.router.slot.activate
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.domain.onramp.CheckOnrampAvailabilityUseCase
import com.tangem.domain.onramp.ClearOnrampCacheUseCase
import com.tangem.domain.onramp.GetOnrampCurrencyUseCase
import com.tangem.domain.onramp.*
import com.tangem.domain.onramp.model.OnrampAvailability
import com.tangem.domain.onramp.model.OnrampCurrency
import com.tangem.features.onramp.main.OnrampMainComponent
@ -16,6 +14,7 @@ import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
import com.tangem.features.onramp.utils.InputManager
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
@ -23,12 +22,16 @@ import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
internal class OnrampMainComponentModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase,
private val getOnrampCurrencyUseCase: GetOnrampCurrencyUseCase,
private val clearOnrampCacheUseCase: ClearOnrampCacheUseCase,
private val fetchQuotesUseCase: OnrampFetchQuotesUseCase,
private val getOnrampQuotesUseCase: GetOnrampQuotesUseCase,
private val amountInputManager: InputManager,
paramsContainer: ParamsContainer,
) : Model(), OnrampIntents {
@ -51,6 +54,7 @@ internal class OnrampMainComponentModel @Inject constructor(
init {
checkResidenceCountry()
subscribeToAmountChanges()
}
private fun checkResidenceCountry() {
@ -73,6 +77,7 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun handleOnrampAvailable(currency: OnrampCurrency) {
_state.update { stateFactory.getReadyState(currency) }
subscribeToCurrencyUpdates()
subscribeToQuotesUpdate()
}
private fun subscribeToCurrencyUpdates() {
@ -86,8 +91,31 @@ internal class OnrampMainComponentModel @Inject constructor(
.launchIn(modelScope)
}
private fun subscribeToAmountChanges() = modelScope.launch {
amountInputManager.query
.filter(String::isNotEmpty)
.collectLatest { _ ->
val content = state.value as? OnrampMainComponentUM.Content ?: return@collectLatest
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
fetchQuotesUseCase.invoke(
amount = content.amountBlockState.amountFieldModel.fiatAmount,
cryptoCurrency = params.cryptoCurrency,
)
}
}
private fun subscribeToQuotesUpdate() {
getOnrampQuotesUseCase.invoke()
.onEach { maybeQuotes ->
val quote = maybeQuotes.getOrNull()?.firstOrNull() ?: return@onEach
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote) }
}
.launchIn(modelScope)
}
override fun onAmountValueChanged(value: String) {
_state.update { amountStateFactory.getOnAmountValueChange(value) }
modelScope.launch { amountInputManager.update(value) }
}
override fun openSettings() {

View file

@ -16,7 +16,7 @@ import com.tangem.features.onramp.selectcountry.entity.CountryListUM
import com.tangem.features.onramp.selectcountry.entity.CountryListUMController
import com.tangem.features.onramp.selectcountry.entity.transformer.UpdateCountryItemsLoadingTransformer
import com.tangem.features.onramp.selectcountry.entity.transformer.UpdateCountryItemsTransformer
import com.tangem.features.onramp.utils.SearchManager
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -30,7 +30,7 @@ import javax.inject.Inject
@ComponentScoped
internal class OnrampSelectCountryModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val searchManager: SearchManager,
private val searchManager: InputManager,
private val getOnrampCountriesUseCase: GetOnrampCountriesUseCase,
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
private val getOnrampCountryUseCase: GetOnrampCountryUseCase,

View file

@ -17,7 +17,7 @@ import com.tangem.features.onramp.selectcurrency.entity.CurrencyItemState
import com.tangem.features.onramp.selectcurrency.entity.CurrencyListController
import com.tangem.features.onramp.selectcurrency.entity.transformer.UpdateCurrencyItemsLoadingTransformer
import com.tangem.features.onramp.selectcurrency.entity.transformer.UpdateCurrencyItemsTransformer
import com.tangem.features.onramp.utils.SearchManager
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -31,7 +31,7 @@ import javax.inject.Inject
@ComponentScoped
internal class OnrampSelectCurrencyModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val searchManager: SearchManager,
private val searchManager: InputManager,
private val getOnrampCurrenciesUseCase: GetOnrampCurrenciesUseCase,
private val saveDefaultCurrencyUseCase: OnrampSaveDefaultCurrencyUseCase,
paramsContainer: ParamsContainer,

View file

@ -23,7 +23,7 @@ import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetLoa
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer
import com.tangem.features.onramp.utils.SearchManager
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -37,7 +37,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
getTokenListUseCase: GetTokenListUseCase,
override val dispatchers: CoroutineDispatcherProvider,
private val tokenListUMController: TokenListUMController,
private val searchManager: SearchManager,
private val searchManager: InputManager,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getAvailablePairsUseCase: GetAvailablePairsUseCase,

View file

@ -20,7 +20,7 @@ import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateTokenItemsTransformer
import com.tangem.features.onramp.utils.SearchManager
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -33,7 +33,7 @@ internal class OnrampTokenListModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val tokenListUMController: TokenListUMController,
private val searchManager: SearchManager,
private val searchManager: InputManager,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getTokenListUseCase: GetTokenListUseCase,

View file

@ -12,7 +12,7 @@ import javax.inject.Inject
*
[REDACTED_AUTHOR]
*/
internal class SearchManager @Inject constructor() {
internal class InputManager @Inject constructor() {
val query: Flow<String>
get() = _query