Updated on 2026-08-14
This commit is contained in:
commit
bc84d8f5f8
579 changed files with 13604 additions and 3422 deletions
|
|
@ -23,13 +23,12 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
|
|||
import com.tangem.domain.tokens.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.tokens.utils.convertToAmount
|
||||
import com.tangem.domain.transaction.TransactionRepository
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.error.SendTransactionError
|
||||
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.EstimateFeeUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
|
|
@ -45,7 +44,6 @@ import com.tangem.lib.crypto.TransactionManager
|
|||
import com.tangem.lib.crypto.UserWalletManager
|
||||
import com.tangem.lib.crypto.models.*
|
||||
import com.tangem.lib.crypto.models.transactions.SendTxResult
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import timber.log.Timber
|
||||
|
|
@ -62,24 +60,19 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private val allowPermissionsHandler: AllowPermissionsHandler,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
private val createTransactionUseCase: CreateTransactionUseCase,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
private val currencyChecksRepository: CurrencyChecksRepository,
|
||||
private val appCurrencyRepository: AppCurrencyRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
private val demoConfig: DemoConfig,
|
||||
private val transactionRepository: TransactionRepository,
|
||||
private val validateTransactionUseCase: ValidateTransactionUseCase,
|
||||
private val estimateFeeUseCase: EstimateFeeUseCase,
|
||||
) : SwapInteractor {
|
||||
|
||||
private val estimateFeeUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
EstimateFeeUseCase(walletManagersFacade, dispatcher)
|
||||
}
|
||||
|
||||
private val getSelectedAppCurrencyUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
GetSelectedAppCurrencyUseCase(appCurrencyRepository)
|
||||
}
|
||||
|
|
@ -494,35 +487,33 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
transactionRepository.validateTransaction(
|
||||
validateTransactionUseCase(
|
||||
amount = amount.value.convertToAmount(fromToken),
|
||||
fee = fee,
|
||||
memo = null,
|
||||
txExtras = null,
|
||||
destination = getTokenAddress(fromToken),
|
||||
userWalletId = userWalletId,
|
||||
network = fromToken.network,
|
||||
)
|
||||
.fold(
|
||||
onFailure = {
|
||||
addCardanoTransactionValidationError(
|
||||
warnings = warnings,
|
||||
error = it as? BlockchainSdkError.Cardano ?: return@fold,
|
||||
fromToken = fromToken,
|
||||
userWalletId = userWalletId,
|
||||
).fold(
|
||||
ifLeft = {
|
||||
addCardanoTransactionValidationError(
|
||||
warnings = warnings,
|
||||
error = it as? BlockchainSdkError.Cardano ?: return@fold,
|
||||
fromToken = fromToken,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
},
|
||||
ifRight = {
|
||||
minAdaValue?.let {
|
||||
warnings.add(
|
||||
Warning.Cardano.MinAdaValueCharged(
|
||||
tokenName = fromToken.name,
|
||||
minAdaValue = minAdaValue.parseBigDecimal(fromToken.decimals),
|
||||
),
|
||||
)
|
||||
},
|
||||
onSuccess = {
|
||||
minAdaValue?.let {
|
||||
warnings.add(
|
||||
Warning.Cardano.MinAdaValueCharged(
|
||||
tokenName = fromToken.name,
|
||||
minAdaValue = minAdaValue.parseBigDecimal(fromToken.decimals),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun addCardanoTransactionValidationError(
|
||||
|
|
@ -745,28 +736,27 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
val exchangeDataCex =
|
||||
exchangeData.transaction as? ExpressTransactionModel.CEX ?: return SwapTransactionState.UnknownError
|
||||
|
||||
val txExtras = transactionManager.getMemoExtras(
|
||||
currencyToSend.currency.network.backendId,
|
||||
exchangeDataCex.txExtraId,
|
||||
)
|
||||
val cardId = getSelectedWallet()?.scanResponse?.card?.cardId ?: return SwapTransactionState.UnknownError
|
||||
if (txExtras == null && exchangeDataCex.txExtraId != null && !demoConfig.isDemoCardId(cardId)) {
|
||||
return SwapTransactionState.UnknownError
|
||||
}
|
||||
if (demoConfig.isDemoCardId(cardId)) return SwapTransactionState.UnknownError
|
||||
|
||||
val txData = createTransactionUseCase(
|
||||
amount = amount.value.convertToAmount(currencyToSend.currency),
|
||||
fee = getFeeForTransaction(
|
||||
fee = txFee,
|
||||
blockchain = Blockchain.fromId(currencyToSend.currency.network.id.value),
|
||||
),
|
||||
memo = null,
|
||||
memo = exchangeDataCex.txExtraId,
|
||||
destination = exchangeDataCex.txTo,
|
||||
userWalletId = userWalletId,
|
||||
network = currencyToSend.currency.network,
|
||||
).getOrElse {
|
||||
Timber.e(it)
|
||||
return SwapTransactionState.UnknownError
|
||||
}.copy(extras = txExtras)
|
||||
}
|
||||
|
||||
if (txData.extras == null && exchangeDataCex.txExtraId != null) {
|
||||
return SwapTransactionState.UnknownError
|
||||
}
|
||||
|
||||
val result = sendTransactionUseCase(
|
||||
txData = txData,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import com.tangem.domain.tokens.repository.NetworksRepository
|
|||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.transaction.TransactionRepository
|
||||
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.EstimateFeeUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
|
|
@ -46,11 +48,10 @@ class SwapDomainModule {
|
|||
swapTransactionRepository: SwapTransactionRepository,
|
||||
appCurrencyRepository: AppCurrencyRepository,
|
||||
currencyChecksRepository: CurrencyChecksRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
coroutineDispatcherProvider: CoroutineDispatcherProvider,
|
||||
initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
transactionRepository: TransactionRepository,
|
||||
validateTransactionUseCase: ValidateTransactionUseCase,
|
||||
estimateFeeUseCase: EstimateFeeUseCase,
|
||||
): SwapInteractor {
|
||||
return SwapInteractorImpl(
|
||||
transactionManager = transactionManager,
|
||||
|
|
@ -62,15 +63,14 @@ class SwapDomainModule {
|
|||
sendTransactionUseCase = sendTransactionUseCase,
|
||||
createTransactionUseCase = createTransactionUseCase,
|
||||
quotesRepository = quotesRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
dispatcher = coroutineDispatcherProvider,
|
||||
swapTransactionRepository = swapTransactionRepository,
|
||||
appCurrencyRepository = appCurrencyRepository,
|
||||
currencyChecksRepository = currencyChecksRepository,
|
||||
currenciesRepository = currenciesRepository,
|
||||
initialToCurrencyResolver = initialToCurrencyResolver,
|
||||
demoConfig = DemoConfig(),
|
||||
transactionRepository = transactionRepository,
|
||||
validateTransactionUseCase = validateTransactionUseCase,
|
||||
estimateFeeUseCase = estimateFeeUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ dependencies {
|
|||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/** Domain modules **/
|
||||
implementation(projects.domain.appCurrency)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package com.tangem.feature.swap.presentation
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.common.routing.AppRouter
|
||||
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.feature.swap.router.CustomTabsManager
|
||||
|
|
@ -28,7 +29,7 @@ class SwapFragment : ComposeFragment() {
|
|||
override lateinit var uiDependencies: UiDependencies
|
||||
|
||||
@Inject
|
||||
lateinit var reduxNavController: ReduxNavController
|
||||
lateinit var appRouter: AppRouter
|
||||
|
||||
private val viewModel by viewModels<SwapViewModel>()
|
||||
|
||||
|
|
@ -37,9 +38,8 @@ class SwapFragment : ComposeFragment() {
|
|||
lifecycle.addObserver(viewModel)
|
||||
viewModel.setRouter(
|
||||
SwapRouter(
|
||||
fragmentManager = WeakReference(parentFragmentManager),
|
||||
customTabsManager = CustomTabsManager(WeakReference(context)),
|
||||
reduxNavController = reduxNavController,
|
||||
router = appRouter,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -47,17 +47,17 @@ class SwapFragment : ComposeFragment() {
|
|||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
viewModel.onScreenOpened()
|
||||
|
||||
val backgroundColor = TangemTheme.colors.background.secondary
|
||||
SystemBarsEffect { setSystemBarsColor(backgroundColor) }
|
||||
|
||||
ScreenContent(viewModel = viewModel)
|
||||
}
|
||||
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
private fun ScreenContent(viewModel: SwapViewModel) {
|
||||
Crossfade(targetState = viewModel.currentScreen, label = "") { screen ->
|
||||
Crossfade(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
targetState = viewModel.currentScreen,
|
||||
label = "",
|
||||
) { screen ->
|
||||
when (screen) {
|
||||
SwapNavScreen.Main -> SwapScreen(stateHolder = viewModel.uiState)
|
||||
SwapNavScreen.Success -> {
|
||||
|
|
@ -84,8 +84,4 @@ class SwapFragment : ComposeFragment() {
|
|||
lifecycle.removeObserver(viewModel)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CURRENCY_BUNDLE_KEY = "swap_currency"
|
||||
}
|
||||
}
|
||||
|
|
@ -3,20 +3,14 @@ package com.tangem.feature.swap.router
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
internal class SwapRouter(
|
||||
private val fragmentManager: WeakReference<FragmentManager>,
|
||||
private val customTabsManager: CustomTabsManager,
|
||||
private val reduxNavController: ReduxNavController,
|
||||
private val router: AppRouter,
|
||||
) {
|
||||
|
||||
var currentScreen by mutableStateOf(SwapNavScreen.Main)
|
||||
|
|
@ -30,7 +24,7 @@ internal class SwapRouter(
|
|||
if (currentScreen == SwapNavScreen.SelectToken) {
|
||||
currentScreen = SwapNavScreen.Main
|
||||
} else {
|
||||
fragmentManager.get()?.popBackStack()
|
||||
router.pop()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,13 +33,10 @@ internal class SwapRouter(
|
|||
}
|
||||
|
||||
fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.NavigateTo(
|
||||
screen = AppScreen.WalletDetails,
|
||||
bundle = bundleOf(
|
||||
TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue,
|
||||
TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency,
|
||||
),
|
||||
router.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = userWalletId,
|
||||
currency = currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.models.states.ChooseFeeBottomSheetConfig
|
||||
import com.tangem.feature.swap.models.states.ChooseProviderBottomSheetConfig
|
||||
|
|
@ -28,7 +29,7 @@ internal fun SwapScreen(stateHolder: SwapStateHolder) {
|
|||
iconRes = R.drawable.ic_close_24,
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0),
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { scaffoldPaddings ->
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import com.tangem.common.Strings.STARS
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
|
|
@ -38,6 +37,7 @@ import com.tangem.feature.swap.models.*
|
|||
import com.tangem.feature.swap.models.states.FeeItemState
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.tangem.utils.Strings.STARS
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import androidx.compose.ui.res.painterResource
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.ExpandableSearchView
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
|
||||
|
|
@ -30,6 +29,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.tangem.utils.Strings
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package com.tangem.feature.swap.viewmodels
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.*
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
|
|
@ -32,7 +35,6 @@ import com.tangem.feature.swap.domain.models.domain.*
|
|||
import com.tangem.feature.swap.domain.models.formatToUIRepresentation
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.presentation.SwapFragment
|
||||
import com.tangem.feature.swap.router.SwapNavScreen
|
||||
import com.tangem.feature.swap.router.SwapRouter
|
||||
import com.tangem.feature.swap.ui.StateBuilder
|
||||
|
|
@ -70,8 +72,10 @@ internal class SwapViewModel @Inject constructor(
|
|||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
private val initialCryptoCurrency: CryptoCurrency =
|
||||
savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY] ?: error("no expected parameter CryptoCurrency found`")
|
||||
private val initialCryptoCurrency: CryptoCurrency = savedStateHandle.get<Bundle>(AppRoute.Swap.CURRENCY_BUNDLE_KEY)
|
||||
?.let { it.unbundle(CryptoCurrency.serializer()) }
|
||||
?: error("no expected parameter CryptoCurrency found`")
|
||||
|
||||
private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
|
||||
private var isBalanceHidden = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue