Updated on 2026-08-14
This commit is contained in:
commit
2541461d44
954 changed files with 22834 additions and 21200 deletions
|
|
@ -4,10 +4,12 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val country: OnrampCountry,
|
||||
val onDismiss: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ internal class DefaultConfirmResidencyComponent @AssistedInject constructor(
|
|||
is ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = SelectCountryComponent.Params(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
onDismiss = { isCountrySelected ->
|
||||
// Dismiss country select sheet
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.features.onramp.deeplink
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.features.onramp.success.OnrampSuccessScreenTrigger
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultOnrampDeepLink @AssistedInject constructor(
|
||||
private val appRouter: AppRouter,
|
||||
private val onrampSuccessScreenTrigger: OnrampSuccessScreenTrigger,
|
||||
@Assisted private val scope: CoroutineScope,
|
||||
) : OnrampDeepLink() {
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
val txId = params[TX_ID_KEY]
|
||||
val result = OnrampRedirectResult.getResult(params[RESULT_KEY])
|
||||
|
||||
when {
|
||||
!txId.isNullOrEmpty() -> {
|
||||
// finish current onramp flow and show onramp success screen
|
||||
val replaceOnrampScreens = appRouter.stack
|
||||
.filterNot { it is AppRoute.Onramp }
|
||||
.toMutableList()
|
||||
replaceOnrampScreens.add(AppRoute.OnrampSuccess(txId))
|
||||
appRouter.replaceAll(*replaceOnrampScreens.toTypedArray())
|
||||
}
|
||||
result != OnrampRedirectResult.Unknown -> {
|
||||
scope.launch {
|
||||
onrampSuccessScreenTrigger.triggerOnrampSuccess(result == OnrampRedirectResult.Success)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : OnrampDeepLink.Factory {
|
||||
override fun create(coroutineScope: CoroutineScope): DefaultOnrampDeepLink
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TX_ID_KEY = "tx_id"
|
||||
const val RESULT_KEY = "result"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.features.onramp.deeplink
|
||||
|
||||
enum class OnrampRedirectResult(val value: String) {
|
||||
Success("success"),
|
||||
Cancel("cancel"),
|
||||
Unknown(""),
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun getResult(result: String?): OnrampRedirectResult {
|
||||
return entries.firstOrNull { it.value == result } ?: Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.features.onramp.deeplink.di
|
||||
|
||||
import com.tangem.features.onramp.deeplink.DefaultOnrampDeepLink
|
||||
import com.tangem.features.onramp.deeplink.OnrampDeepLink
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface OnrampDeeplinkModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindFactory(impl: DefaultOnrampDeepLink.Factory): OnrampDeepLink.Factory
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
|||
is OnrampMainBottomSheetConfig.ConfirmResidency -> confirmResidencyComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = ConfirmResidencyComponent.Params(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
country = config.country,
|
||||
onDismiss = {
|
||||
|
|
@ -65,6 +66,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
|
|||
is OnrampMainBottomSheetConfig.CurrenciesList -> selectCurrencyComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = SelectCurrencyComponent.Params(
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
) : Model(), OnrampIntents {
|
||||
|
||||
private val params: OnrampMainComponent.Params = paramsContainer.require()
|
||||
|
||||
val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
|
||||
private val stateFactory = OnrampStateFactory(
|
||||
currentStateProvider = Provider { _state.value },
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
|
|
@ -68,7 +71,6 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
onrampIntents = this,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
)
|
||||
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
|
||||
value = stateFactory.getInitialState(
|
||||
currency = params.cryptoCurrency.name,
|
||||
|
|
@ -107,7 +109,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
|
||||
private fun checkResidenceCountry() {
|
||||
modelScope.launch {
|
||||
checkOnrampAvailabilityUseCase()
|
||||
checkOnrampAvailabilityUseCase(userWallet)
|
||||
.onRight(::handleOnrampAvailability)
|
||||
.onLeft(::handleOnrampError)
|
||||
}
|
||||
|
|
@ -158,7 +160,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
if (!state?.amountBlockState?.amountFieldModel?.fiatValue.isNullOrEmpty()) {
|
||||
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
|
||||
}
|
||||
fetchPairsUseCase.invoke(params.cryptoCurrency).fold(
|
||||
fetchPairsUseCase.invoke(userWallet, params.cryptoCurrency).fold(
|
||||
ifLeft = ::handleOnrampError,
|
||||
ifRight = { _state.update { amountStateFactory.getAmountSecondaryResetState() } },
|
||||
)
|
||||
|
|
@ -184,6 +186,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
val content = state.value as? OnrampMainComponentUM.Content ?: return@runCatching
|
||||
if (content.amountBlockState.amountFieldModel.fiatAmount.value.isNullOrZero()) return@runCatching
|
||||
fetchQuotesUseCase.invoke(
|
||||
userWallet = userWallet,
|
||||
amount = content.amountBlockState.amountFieldModel.fiatAmount,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
).onLeft(::handleOnrampError)
|
||||
|
|
@ -216,7 +219,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBuyClick(quote: OnrampProviderWithQuote.Data) {
|
||||
if (isDemoCardUseCase.invoke(selectedUserWallet.cardId)) {
|
||||
if (isDemoCardUseCase.invoke(userWallet.cardId)) {
|
||||
showDemoWarning()
|
||||
} else {
|
||||
val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return
|
||||
|
|
|
|||
|
|
@ -42,16 +42,13 @@ internal fun OnrampAmountContent(state: OnrampAmountBlockUM, modifier: Modifier
|
|||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
OnrampCurrencyIcon(currencyUM = state.currencyUM)
|
||||
OnrampAmountField(
|
||||
amountField = state.amountFieldModel,
|
||||
isLoading = state.secondaryFieldModel is OnrampAmountSecondaryFieldUM.Loading,
|
||||
)
|
||||
OnrampAmountField(amountField = state.amountFieldModel)
|
||||
OnrampAmountSecondary(state = state.secondaryFieldModel)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnrampAmountField(amountField: AmountFieldModel, isLoading: Boolean) {
|
||||
private fun OnrampAmountField(amountField: AmountFieldModel) {
|
||||
val decimalFormat = rememberDecimalFormat()
|
||||
val requester = remember { FocusRequester() }
|
||||
AmountTextField(
|
||||
|
|
@ -70,7 +67,7 @@ private fun OnrampAmountField(amountField: AmountFieldModel, isLoading: Boolean)
|
|||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
isEnabled = !amountField.isError && !isLoading,
|
||||
isEnabled = !amountField.isError,
|
||||
isAutoResize = true,
|
||||
isValuePasted = amountField.isValuePasted,
|
||||
onValuePastedTriggerDismiss = amountField.onValuePastedTriggerDismiss,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package com.tangem.features.onramp.redirect.model
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
|
|
@ -13,13 +14,18 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.domain.onramp.GetOnrampRedirectUrlUseCase
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.redirect.OnrampRedirectComponent
|
||||
import com.tangem.features.onramp.redirect.entity.OnrampRedirectTopBarUM
|
||||
import com.tangem.features.onramp.redirect.entity.OnrampRedirectUM
|
||||
import com.tangem.features.onramp.success.OnrampSuccessScreenListener
|
||||
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -31,11 +37,17 @@ internal class OnrampRedirectModel @Inject constructor(
|
|||
private val getOnrampRedirectUrlUseCase: GetOnrampRedirectUrlUseCase,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
router: Router,
|
||||
private val onrampSuccessScreenListener: OnrampSuccessScreenListener,
|
||||
private val appRouter: AppRouter,
|
||||
paramsContainer: ParamsContainer,
|
||||
getWalletsUseCase: GetWalletsUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params: OnrampRedirectComponent.Params = paramsContainer.require()
|
||||
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
|
||||
private var latestOnrampTransaction: OnrampTransaction? = null
|
||||
|
||||
val state = OnrampRedirectUM(
|
||||
topBarConfig = OnrampRedirectTopBarUM(
|
||||
title = combinedReference(
|
||||
|
|
@ -44,7 +56,7 @@ internal class OnrampRedirectModel @Inject constructor(
|
|||
),
|
||||
startButtonUM = TopAppBarButtonUM(
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
onIconClicked = router::pop,
|
||||
onIconClicked = appRouter::pop,
|
||||
enabled = true,
|
||||
),
|
||||
),
|
||||
|
|
@ -59,22 +71,45 @@ internal class OnrampRedirectModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
init {
|
||||
subscribeToOnrampSuccessListener()
|
||||
}
|
||||
|
||||
fun getRedirectUrl(isDarkTheme: Boolean) {
|
||||
modelScope.launch {
|
||||
getOnrampRedirectUrlUseCase.invoke(
|
||||
userWalletId = params.userWalletId,
|
||||
userWallet = selectedUserWallet,
|
||||
quote = params.onrampProviderWithQuote,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
isDarkTheme = isDarkTheme,
|
||||
)
|
||||
.onLeft(::handleError)
|
||||
.onRight {
|
||||
params.onBack()
|
||||
urlOpener.openUrl(it)
|
||||
latestOnrampTransaction = it
|
||||
urlOpener.openUrl(it.redirectUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToOnrampSuccessListener() {
|
||||
onrampSuccessScreenListener.onrampSuccessTriggerFlow
|
||||
.onEach { result ->
|
||||
val latestTxId = latestOnrampTransaction?.txId
|
||||
if (latestTxId != null && result) {
|
||||
// Finish current onramp flow and show onramp success screen
|
||||
val replaceOnrampScreens = appRouter.stack
|
||||
.filterNot { it is AppRoute.Onramp }
|
||||
.toMutableList()
|
||||
replaceOnrampScreens.add(AppRoute.OnrampSuccess(latestTxId))
|
||||
appRouter.replaceAll(*replaceOnrampScreens.toTypedArray())
|
||||
} else {
|
||||
// Close redirect screen and show last onramp state
|
||||
params.onBack()
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun handleError(error: OnrampError) {
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.sendOnrampErrorEvent(
|
||||
|
|
|
|||
|
|
@ -61,8 +61,9 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
|
|||
OnrampChild.Settings -> settingsComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = OnrampSettingsComponent.Params(
|
||||
params.cryptoCurrency,
|
||||
navigation::pop,
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
onBack = navigation::pop,
|
||||
),
|
||||
)
|
||||
OnrampChild.Main -> onrampMainComponentFactory.create(
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.selectcountry
|
|||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal interface SelectCountryComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: (Boolean) -> Unit)
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val onDismiss: (Boolean) -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, SelectCountryComponent>
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.onramp.GetOnrampCountryUseCase
|
|||
import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase
|
||||
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
||||
import com.tangem.domain.onramp.model.OnrampCountry
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.selectcountry.SelectCountryComponent
|
||||
import com.tangem.features.onramp.selectcountry.entity.CountryItemState
|
||||
|
|
@ -44,11 +45,15 @@ internal class OnrampSelectCountryModel @Inject constructor(
|
|||
private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase,
|
||||
private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
|
||||
private val fetchOnrampCountriesUseCase: FetchOnrampCountriesUseCase,
|
||||
getWalletsUseCase: GetWalletsUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
val state: StateFlow<CountryListUM> get() = controller.state
|
||||
private val params: SelectCountryComponent.Params = paramsContainer.require()
|
||||
private val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
|
||||
val state: StateFlow<CountryListUM> get() = controller.state
|
||||
|
||||
private val controller = CountryListUMController(
|
||||
searchBarUM = createSearchBarUM(),
|
||||
loadingItems = loadingItems,
|
||||
|
|
@ -103,7 +108,7 @@ internal class OnrampSelectCountryModel @Inject constructor(
|
|||
|
||||
private fun updateCountriesList() {
|
||||
modelScope.launch {
|
||||
fetchOnrampCountriesUseCase().onLeft {
|
||||
fetchOnrampCountriesUseCase(userWallet).onLeft {
|
||||
controller.update(UpdateCountryItemsErrorTransformer(onRetry = ::onRetry))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.selectcurrency
|
|||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
||||
internal interface SelectCurrencyComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: () -> Unit)
|
||||
data class Params(
|
||||
val userWallet: UserWallet,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, SelectCurrencyComponent>
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ internal class OnrampSelectCurrencyModel @Inject constructor(
|
|||
|
||||
private fun updateCurrenciesList() {
|
||||
modelScope.launch {
|
||||
fetchOnrampCurrenciesUseCase().onLeft {
|
||||
fetchOnrampCurrenciesUseCase(userWallet = params.userWallet).onLeft {
|
||||
controller.update(UpdateCurrencyItemsErrorTransformer(onRetry = ::onRetry))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ internal class DefaultOnrampSettingsComponent @AssistedInject constructor(
|
|||
OnrampSettingsConfig.SelectCountry -> selectCountryComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = SelectCountryComponent.Params(
|
||||
params.cryptoCurrency,
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,15 @@ package com.tangem.features.onramp.settings
|
|||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal interface OnrampSettingsComponent : ComposableContentComponent {
|
||||
|
||||
data class Params(val cryptoCurrency: CryptoCurrency, val onBack: () -> Unit)
|
||||
data class Params(
|
||||
val userWalletId: UserWalletId,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val onBack: () -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnrampSettingsComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.onramp.success
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface OnrampSuccessScreenListener {
|
||||
val onrampSuccessTriggerFlow: Flow<Boolean>
|
||||
}
|
||||
|
||||
interface OnrampSuccessScreenTrigger {
|
||||
suspend fun triggerOnrampSuccess(result: Boolean)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultOnrampSuccessScreenTrigger @Inject constructor() :
|
||||
OnrampSuccessScreenTrigger,
|
||||
OnrampSuccessScreenListener {
|
||||
|
||||
override val onrampSuccessTriggerFlow = MutableSharedFlow<Boolean>()
|
||||
|
||||
override suspend fun triggerOnrampSuccess(result: Boolean) {
|
||||
onrampSuccessTriggerFlow.emit(result)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ package com.tangem.features.onramp.success.di
|
|||
|
||||
import com.tangem.features.onramp.component.OnrampSuccessComponent
|
||||
import com.tangem.features.onramp.success.DefaultOnrampSuccessComponent
|
||||
import com.tangem.features.onramp.success.DefaultOnrampSuccessScreenTrigger
|
||||
import com.tangem.features.onramp.success.OnrampSuccessScreenListener
|
||||
import com.tangem.features.onramp.success.OnrampSuccessScreenTrigger
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -16,4 +19,12 @@ internal interface OnrampSuccessComponentModule {
|
|||
fun bindOnrampSuccessComponentFactory(
|
||||
factory: DefaultOnrampSuccessComponent.Factory,
|
||||
): OnrampSuccessComponent.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindOnrampSuccessScreenTrigger(impl: DefaultOnrampSuccessScreenTrigger): OnrampSuccessScreenTrigger
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindOnrampSuccessScreenListener(impl: DefaultOnrampSuccessScreenTrigger): OnrampSuccessScreenListener
|
||||
}
|
||||
|
|
@ -2,4 +2,5 @@ package com.tangem.features.onramp.success.entity
|
|||
|
||||
interface OnrampSuccessClickIntents {
|
||||
fun goToProviderClick(providerLink: String)
|
||||
fun onCopyClick(copiedText: String)
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ internal class SetOnrampSuccessContentConverter(
|
|||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val transaction: OnrampTransaction,
|
||||
private val goToProviderClick: (String) -> Unit,
|
||||
private val onCopyClick: (String) -> Unit,
|
||||
) : Converter<OnrampStatus, OnrampSuccessComponentUM> {
|
||||
override fun convert(value: OnrampStatus): OnrampSuccessComponentUM {
|
||||
return OnrampSuccessComponentUM.Content(
|
||||
|
|
@ -46,8 +47,12 @@ internal class SetOnrampSuccessContentConverter(
|
|||
),
|
||||
providerName = stringReference(transaction.providerName),
|
||||
providerImageUrl = transaction.providerImageUrl,
|
||||
statusBlock = convertStatuses(value.status, value.externalTxUrl),
|
||||
notification = getNotification(value.status, value.externalTxUrl),
|
||||
statusBlock = convertStatuses(
|
||||
status = value.status,
|
||||
externalTxId = value.externalTxId,
|
||||
externalTxUrl = value.externalTxUrl,
|
||||
),
|
||||
notification = getNotification(status = value.status, externalTxUrl = value.externalTxUrl),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +78,11 @@ internal class SetOnrampSuccessContentConverter(
|
|||
null
|
||||
}
|
||||
|
||||
private fun convertStatuses(status: OnrampStatus.Status, externalTxUrl: String?): ExpressStatusUM {
|
||||
private fun convertStatuses(
|
||||
status: OnrampStatus.Status,
|
||||
externalTxId: String?,
|
||||
externalTxUrl: String?,
|
||||
): ExpressStatusUM {
|
||||
val statuses = with(status) {
|
||||
persistentListOf(
|
||||
getAwaitingDepositItem(),
|
||||
|
|
@ -85,7 +94,7 @@ internal class SetOnrampSuccessContentConverter(
|
|||
|
||||
return ExpressStatusUM(
|
||||
title = resourceReference(R.string.common_transaction_status),
|
||||
link = getStatusLink(status, externalTxUrl),
|
||||
link = getStatusLink(status = status, externalTxId = externalTxId, externalTxUrl = externalTxUrl),
|
||||
statuses = statuses,
|
||||
)
|
||||
}
|
||||
|
|
@ -148,6 +157,10 @@ internal class SetOnrampSuccessContentConverter(
|
|||
this == OnrampStatus.Status.Paid -> {
|
||||
resourceReference(R.string.express_status_buying_active, wrappedList(cryptoCurrency.name))
|
||||
}
|
||||
this == OnrampStatus.Status.RefundInProgress ||
|
||||
this == OnrampStatus.Status.Refunded -> {
|
||||
resourceReference(R.string.express_exchange_status_failed)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(R.string.express_status_bought, wrappedList(cryptoCurrency.name))
|
||||
}
|
||||
|
|
@ -166,6 +179,12 @@ internal class SetOnrampSuccessContentConverter(
|
|||
wrappedList(cryptoCurrency.name),
|
||||
)
|
||||
}
|
||||
this == OnrampStatus.Status.RefundInProgress -> {
|
||||
resourceReference(R.string.express_exchange_status_refunding)
|
||||
}
|
||||
this == OnrampStatus.Status.Refunded -> {
|
||||
resourceReference(R.string.express_exchange_status_refunded)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(R.string.express_exchange_status_sent, wrappedList(cryptoCurrency.name))
|
||||
}
|
||||
|
|
@ -173,7 +192,11 @@ internal class SetOnrampSuccessContentConverter(
|
|||
state = getStatusState(OnrampStatus.Status.Sending),
|
||||
)
|
||||
|
||||
private fun getStatusLink(status: OnrampStatus.Status, externalTxUrl: String?): ExpressLinkUM {
|
||||
private fun getStatusLink(
|
||||
status: OnrampStatus.Status,
|
||||
externalTxId: String?,
|
||||
externalTxUrl: String?,
|
||||
): ExpressLinkUM {
|
||||
if (externalTxUrl == null) return ExpressLinkUM.Empty
|
||||
return when (status) {
|
||||
OnrampStatus.Status.Verifying,
|
||||
|
|
@ -187,7 +210,15 @@ internal class SetOnrampSuccessContentConverter(
|
|||
},
|
||||
)
|
||||
}
|
||||
else -> ExpressLinkUM.Empty
|
||||
else -> if (externalTxId != null) {
|
||||
ExpressLinkUM.Content(
|
||||
icon = R.drawable.ic_copy_24,
|
||||
text = resourceReference(R.string.express_transaction_id, wrappedList(externalTxId)),
|
||||
onClick = { onCopyClick(externalTxId) },
|
||||
)
|
||||
} else {
|
||||
ExpressLinkUM.Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
|
|
@ -46,6 +47,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val router: Router,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), OnrampSuccessClickIntents {
|
||||
|
|
@ -65,9 +67,13 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
urlOpener.openUrl(providerLink)
|
||||
}
|
||||
|
||||
override fun onCopyClick(copiedText: String) {
|
||||
clipboardManager.setText(text = copiedText, isSensitive = false)
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
modelScope.launch {
|
||||
getOnrampTransactionUseCase(externalTxId = params.externalTxId)
|
||||
getOnrampTransactionUseCase(txId = params.txId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
|
|
@ -97,7 +103,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
getOnrampStatusUseCase(txId = transaction.txId)
|
||||
getOnrampStatusUseCase(userWallet = userWallet, txId = transaction.txId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
analyticsEventHandler.sendOnrampErrorEvent(
|
||||
|
|
@ -124,6 +130,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
cryptoCurrency = cryptoCurrency,
|
||||
transaction = transaction,
|
||||
goToProviderClick = ::goToProviderClick,
|
||||
onCopyClick = ::onCopyClick,
|
||||
).convert(status)
|
||||
}
|
||||
removeTransactionIfTerminalStatus(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.domain.tokens.GetTokenListUseCase
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
|
||||
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapPairLeast
|
||||
|
|
@ -52,11 +53,13 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getAvailablePairsUseCase: GetAvailablePairsUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
) : Model() {
|
||||
|
||||
val state: StateFlow<TokenListUM> = tokenListUMController.state
|
||||
|
||||
private var params: AvailableSwapPairsComponent.Params = paramsContainer.require()
|
||||
private val userWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
|
||||
private val tokenListFlow = getTokenListUseCaseFlow()
|
||||
|
||||
|
|
@ -212,6 +215,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
availablePairsByNetworkFlow.update(networkInfo = networkInfo, state = lceLoading())
|
||||
|
||||
getAvailablePairsUseCase(
|
||||
userWallet = userWallet,
|
||||
initialCurrency = networkInfo,
|
||||
currencies = statuses.map(CryptoCurrencyStatus::currency),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue