From 88ab462aa7d13f5c6d1b6f5b5612aef6d0d2abab Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 15 Dec 2025 15:20:29 +0200 Subject: [PATCH] Updated on 2026-08-14 --- detekt_baseline_report.txt | 15 +- features/swap/data/detekt-baseline-debug.xml | 21 -- .../feature/swap/DefaultSwapRepository.kt | 18 +- .../swap/DefaultSwapTransactionRepository.kt | 46 ++-- .../swap/converters/ExpressDataConverter.kt | 6 +- .../swap/domain/detekt-baseline-debug.xml | 31 --- .../feature/swap/domain/SwapInteractorImpl.kt | 172 +++++++++------ features/swap/impl/detekt-baseline-debug.xml | 73 ------- .../feature/swap/analytics/StoriesEvents.kt | 2 +- .../feature/swap/analytics/SwapEvents.kt | 10 +- .../converters/AccountTokenItemConverter.kt | 14 +- .../swap/converters/TokensDataConverter.kt | 4 +- .../swap/converters/TokensDataConverterV2.kt | 2 +- .../tangem/feature/swap/model/SwapModel.kt | 200 ++++++++++-------- .../swap/model/SwapNotificationsFactory.kt | 7 +- .../swap/models/SwapSelectTokenStateHolder.kt | 4 +- .../feature/swap/models/SwapStateHolder.kt | 4 +- .../swap/models/SwapSuccessStateHolder.kt | 2 +- .../swap/preview/SwapSuccessStatePreview.kt | 2 +- .../feature/swap/ui/ChooseFeeBottomSheet.kt | 13 +- .../tangem/feature/swap/ui/ProviderItem.kt | 28 +-- .../tangem/feature/swap/ui/StateBuilder.kt | 77 +++---- .../feature/swap/ui/SwapScreenContent.kt | 6 +- .../feature/swap/ui/SwapSelectTokenScreen.kt | 12 +- .../feature/swap/ui/SwapSuccessScreen.kt | 6 +- .../tangem/feature/swap/ui/TransactionCard.kt | 18 +- 26 files changed, 365 insertions(+), 428 deletions(-) delete mode 100644 features/swap/data/detekt-baseline-debug.xml delete mode 100644 features/swap/domain/detekt-baseline-debug.xml delete mode 100644 features/swap/impl/detekt-baseline-debug.xml diff --git a/detekt_baseline_report.txt b/detekt_baseline_report.txt index a45e2bff61..d36313f946 100644 --- a/detekt_baseline_report.txt +++ b/detekt_baseline_report.txt @@ -1,7 +1,7 @@ ========================================== Detekt Baseline Updater & Issue Counter ========================================== -Date: 2025-12-12 13:17:20 +Date: 2025-12-13 00:34:09 Step 1: Running detekt to check for new issues... @@ -17,13 +17,13 @@ Counting issues in baseline files... ========================================== Summary: - Total Issues: 1378 - Modules with Issues: 72 - Average Issues per Module: 19 + Total Issues: 1271 + Modules with Issues: 69 + Average Issues per Module: 18 Progress: - Fixed: 555 out of 1933 (28%) - Remaining: 1378 + Fixed: 662 out of 1933 (34%) + Remaining: 1271 ========================================== All Modules with Issues (sorted by count) @@ -34,7 +34,6 @@ Module Issues features/markets/impl 147 features/wallet/impl 144 features/onboarding-v2/impl 128 -features/swap/impl 67 data/wallet-connect 54 features/hot-wallet/impl 51 features/walletconnect/impl 47 @@ -45,7 +44,6 @@ domain/wallets 37 features/nft/impl 34 features/tester/impl 28 core/ui 26 -features/swap/domain 25 domain/tokens 25 common/ui 24 features/yield-supply/impl 21 @@ -54,7 +52,6 @@ domain/models 21 data/visa 21 data/nft 19 core/pagination 16 -features/swap/data 15 domain/staking/models 13 data/wallets 13 features/token-recieve/impl 11 diff --git a/features/swap/data/detekt-baseline-debug.xml b/features/swap/data/detekt-baseline-debug.xml deleted file mode 100644 index 640fd2fbc1..0000000000 --- a/features/swap/data/detekt-baseline-debug.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - BooleanPropertyNaming:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository$val fromCurrency = it.fromCryptoCurrencyId == cryptoCurrencyId.value - BooleanPropertyNaming:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository$val toCurrency = it.toCryptoCurrencyId == cryptoCurrencyId.value - MultilineLambdaItParameter:DefaultSwapRepository.kt$DefaultSwapRepository${ Timber.e("getExchangeStatus error: $it") raise(UnknownError(it.message)) } - MultilineLambdaItParameter:DefaultSwapRepository.kt$DefaultSwapRepository${ val requirements = walletManagersFacade.getAssetRequirements(userWallet.walletId, it) val isAvailableForSwap = rampStateManager.checkAssetRequirements(requirements) isAvailableForSwap } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ converter.convertBack( value = it, userWallet = userWallet, accountList = accountList, txStatuses = txStatuses, ) } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ converter.convertBack( value = it, userWallet = userWallet, accountList = accountList, txStatuses = txStatuses, onFilter = { it.swapTxTypeDTO == SwapTxTypeDTO.Swap }, ) } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ it.checkId( checkUserWalletId = userWalletId, fromCurrencyId = fromCryptoCurrency.id, toCurrencyId = toCryptoCurrency.id, ) } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ storeTransactionState( txId = transaction.txId, status = it, accountWithCurrency = null, ) } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ val isUserWallet = it.userWalletId == userWallet.walletId.stringValue val fromCurrency = it.fromCryptoCurrencyId == cryptoCurrencyId.value isUserWallet && fromCurrency } - MultilineLambdaItParameter:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ val isUserWallet = it.userWalletId == userWallet.walletId.stringValue val toCurrency = it.toCryptoCurrencyId == cryptoCurrencyId.value isUserWallet && toCurrency } - MultilineLambdaItParameter:ExpressDataConverter.kt$ExpressDataConverter${ if (it == "0") { BigDecimal.ZERO } else { requireNotNull(it.toBigDecimalOrNull()) { "wrong amount format, use only digits" } } } - NoNameShadowing:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ it.swapTxTypeDTO == SwapTxTypeDTO.Swap } - NoNameShadowing:DefaultSwapTransactionRepository.kt$DefaultSwapTransactionRepository${ it.txId == txId } - UseOrEmpty:DefaultSwapRepository.kt$DefaultSwapRepository$ex.errorBody ?: "" - UseOrEmpty:DefaultSwapRepository.kt$DefaultSwapRepository$exception.errorBody ?: "" - - diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index 2f34e89a84..2b464a5d59 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -118,7 +118,7 @@ internal class DefaultSwapRepository( ) } catch (exception: Exception) { if (exception is ApiResponseError.HttpException) { - throw ExpressException(errorsDataConverter.convert(exception.errorBody ?: "")) + throw ExpressException(errorsDataConverter.convert(exception.errorBody.orEmpty())) } else { throw exception } @@ -138,12 +138,12 @@ internal class DefaultSwapRepository( network = initialCurrency.network, ) val currenciesList = currencyList - .filter { - val requirements = walletManagersFacade.getAssetRequirements(userWallet.walletId, it) + .filter { currency -> + val requirements = walletManagersFacade.getAssetRequirements(userWallet.walletId, currency) val isAvailableForSwap = rampStateManager.checkAssetRequirements(requirements) isAvailableForSwap } - .map { leastTokenInfoConverter.convert(it) } + .map { currency -> leastTokenInfoConverter.convert(currency) } val pairsDeferred = async { getPairsInternal( @@ -174,7 +174,7 @@ internal class DefaultSwapRepository( ) } catch (exception: Exception) { if (exception is ApiResponseError.HttpException) { - throw ExpressException(errorsDataConverter.convert(exception.errorBody ?: "")) + throw ExpressException(errorsDataConverter.convert(exception.errorBody.orEmpty())) } else { throw exception } @@ -221,9 +221,9 @@ internal class DefaultSwapRepository( .getOrThrow(), ) }, - catch = { - Timber.e("getExchangeStatus error: $it") - raise(UnknownError(it.message)) + catch = { exception -> + Timber.e("getExchangeStatus error: $exception") + raise(UnknownError(exception.message)) }, ) } @@ -420,7 +420,7 @@ internal class DefaultSwapRepository( private fun getDataError(ex: Exception): ExpressDataError { return if (ex is ApiResponseError.HttpException) { - errorsDataConverter.convert(ex.errorBody ?: "") + errorsDataConverter.convert(ex.errorBody.orEmpty()) } else { ExpressDataError.UnknownError } diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index ab4f58a5df..fb7b9d0ed1 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -51,10 +51,10 @@ internal class DefaultSwapTransactionRepository( toAccount: Account.CryptoPortfolio?, transaction: SavedSwapTransactionModel, ) { - transaction.status?.let { + transaction.status?.let { status -> storeTransactionState( txId = transaction.txId, - status = it, + status = status, accountWithCurrency = null, ) } @@ -63,8 +63,8 @@ internal class DefaultSwapTransactionRepository( key = PreferencesKeys.SWAP_TRANSACTIONS_KEY, ) val tokenTransactions = savedTransactions - ?.firstOrNull { - it.checkId( + ?.firstOrNull { savedTx -> + savedTx.checkId( checkUserWalletId = userWalletId, fromCurrencyId = fromCryptoCurrency.id, toCurrencyId = toCryptoCurrency.id, @@ -73,7 +73,7 @@ internal class DefaultSwapTransactionRepository( ?.transactions ?.addOrReplace( item = transaction, - predicate = { it.txId == transaction.txId }, + predicate = { tx -> tx.txId == transaction.txId }, ) ?: listOf(transaction) mutablePreferences.setObject( @@ -117,31 +117,31 @@ internal class DefaultSwapTransactionRepository( }, ) { savedTransactions, txStatuses, accountList -> - val currencyToTxs = savedTransactions?.filter { - val isUserWallet = it.userWalletId == userWallet.walletId.stringValue - val toCurrency = it.toCryptoCurrencyId == cryptoCurrencyId.value - isUserWallet && toCurrency + val currencyToTxs = savedTransactions?.filter { savedTx -> + val isUserWallet = savedTx.userWalletId == userWallet.walletId.stringValue + val isToCurrency = savedTx.toCryptoCurrencyId == cryptoCurrencyId.value + isUserWallet && isToCurrency } - val currencyFromTxs = savedTransactions?.filter { - val isUserWallet = it.userWalletId == userWallet.walletId.stringValue - val fromCurrency = it.fromCryptoCurrencyId == cryptoCurrencyId.value - isUserWallet && fromCurrency + val currencyFromTxs = savedTransactions?.filter { savedTx -> + val isUserWallet = savedTx.userWalletId == userWallet.walletId.stringValue + val isFromCurrency = savedTx.fromCryptoCurrencyId == cryptoCurrencyId.value + isUserWallet && isFromCurrency } - val toTxs = currencyToTxs?.mapNotNull { + val toTxs = currencyToTxs?.mapNotNull { savedTx -> converter.convertBack( - value = it, + value = savedTx, userWallet = userWallet, accountList = accountList, txStatuses = txStatuses, - onFilter = { it.swapTxTypeDTO == SwapTxTypeDTO.Swap }, + onFilter = { tx -> tx.swapTxTypeDTO == SwapTxTypeDTO.Swap }, ) }.orEmpty() - val fromTxs = currencyFromTxs?.mapNotNull { + val fromTxs = currencyFromTxs?.mapNotNull { savedTx -> converter.convertBack( - value = it, + value = savedTx, userWallet = userWallet, accountList = accountList, txStatuses = txStatuses, @@ -161,9 +161,9 @@ internal class DefaultSwapTransactionRepository( ) val tokenTransactions = savedList ?.asSequence() - ?.map { - it.copy(transactions = it.transactions.filterNot { it.txId == txId }) - }?.filterNot { it.transactions.isEmpty() } + ?.map { savedTx -> + savedTx.copy(transactions = savedTx.transactions.filterNot { tx -> tx.txId == txId }) + }?.filterNot { savedTx -> savedTx.transactions.isEmpty() } ?.toList() if (tokenTransactions.isNullOrEmpty()) { @@ -261,8 +261,8 @@ internal class DefaultSwapTransactionRepository( toAccount = toAccount, tokenTransactions = transactions, ), - predicate = { - it.checkId( + predicate = { savedTx -> + savedTx.checkId( checkUserWalletId = userWalletId, fromCurrencyId = fromCryptoCurrency.id, toCurrencyId = toCryptoCurrency.id, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt index 34c663840b..8d605d8dfd 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ExpressDataConverter.kt @@ -25,11 +25,11 @@ internal class ExpressDataConverter : Converter + if (feeValue == "0") { BigDecimal.ZERO } else { - requireNotNull(it.toBigDecimalOrNull()) { "wrong amount format, use only digits" } + requireNotNull(feeValue.toBigDecimalOrNull()) { "wrong amount format, use only digits" } } } ExpressTransactionModel.DEX( diff --git a/features/swap/domain/detekt-baseline-debug.xml b/features/swap/domain/detekt-baseline-debug.xml deleted file mode 100644 index e1d3527ea0..0000000000 --- a/features/swap/domain/detekt-baseline-debug.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - BooleanPropertyNaming:SwapInteractorImpl.kt$SwapInteractorImpl$val allQuotesFound = cachedQuotes?.all { it.value !is QuoteStatus.Empty } == true - BooleanPropertyNaming:SwapInteractorImpl.kt$SwapInteractorImpl$val currencyFilter = it.currency.network.backendId != currency.network.backendId || it.currency.getContractAddress() != currency.getContractAddress() - BooleanPropertyNaming:SwapInteractorImpl.kt$SwapInteractorImpl$val notCustomTokenFilter = !it.currency.isCustom - BooleanPropertyNaming:SwapInteractorImpl.kt$SwapInteractorImpl$val statusFilter = it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount - CanBeNonNullable:SwapInteractorImpl.kt$SwapInteractorImpl$spenderAddress: String? - MaxChainedCallsOnSameLine:SwapInteractorImpl.kt$SwapInteractorImpl$currencyToGet.value.networkAddress?.defaultAddress?.value.orEmpty() - MaxChainedCallsOnSameLine:SwapInteractorImpl.kt$SwapInteractorImpl$currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty() - MaxChainedCallsOnSameLine:SwapInteractorImpl.kt$SwapInteractorImpl$currencyToSendStatus.value.networkAddress?.defaultAddress?.value.orEmpty() - MaxChainedCallsOnSameLine:SwapInteractorImpl.kt$SwapInteractorImpl$fromToken.value.networkAddress?.defaultAddress?.value.orEmpty() - MaxChainedCallsOnSameLine:SwapInteractorImpl.kt$SwapInteractorImpl$toToken.value.networkAddress?.defaultAddress?.value.orEmpty() - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ Timber.e(it, "Failed to create approveTransaction") return SwapTransactionState.Error.UnknownError } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ Timber.e(it, "Failed to create swap CEX tx data") return SwapTransactionState.Error.UnknownError } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ Timber.e(it, "Failed to create swap dex tx data") return SwapTransactionState.Error.UnknownError } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ it.contractAddress.equals(feePaidCurrency.contractAddress, ignoreCase = true) && it.network.derivationPath == fromTokenStatus.currency.network.derivationPath } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ tokenInfoForFilter(it).contractAddress == currency.getContractAddress() && tokenInfoForFilter(it).network == currency.network.backendId } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ val currencyFilter = it.currency.network.backendId != currency.network.backendId || it.currency.getContractAddress() != currency.getContractAddress() val statusFilter = it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount val notCustomTokenFilter = !it.currency.isCustom statusFilter && currencyFilter && notCustomTokenFilter } - MultilineLambdaItParameter:SwapInteractorImpl.kt$SwapInteractorImpl${ val listTokenInfo = tokenInfoForAvailable(it) if (cryptoCurrencyStatuses.currency.network.backendId == listTokenInfo.network && cryptoCurrencyStatuses.currency.getContractAddress() == listTokenInfo.contractAddress && isAvailableForSwap ) { it.providers } else { null } } - NamedArguments:SwapInteractorImpl.kt$SwapInteractorImpl$getCoinBalanceAfterTransaction(fromTokenStatus, amount, includeFeeInAmount, fee) - NamedArguments:SwapInteractorImpl.kt$SwapInteractorImpl$isAllowedToSpend(networkId, fromToken.currency, amount, it) - NamedArguments:SwapInteractorImpl.kt$SwapInteractorImpl$tryGetFromCache(userWallet, initialCryptoCurrency, state, isReverseFromTo) - NamedArguments:SwapInteractorImpl.kt$SwapInteractorImpl$tryGetFromCacheV2(userWallet, initialCryptoCurrency, state, isReverseFromTo) - NoNameShadowing:SwapInteractorImpl.kt$SwapInteractorImpl$account - NoNameShadowing:SwapInteractorImpl.kt$SwapInteractorImpl${ it.isAvailable } - NullableToStringCall:SwapInteractorImpl.kt$SwapInteractorImpl$${e.message} - SuspendFunSwallowedCancellation:SwapInteractorImpl.kt$SwapInteractorImpl$runCatching - - diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index aa8415a3c9..fc9b3e26de 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -43,6 +43,7 @@ import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.* import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.utils.coroutines.runSuspendCatching import com.tangem.feature.swap.domain.api.SwapRepository import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount @@ -122,12 +123,13 @@ internal class SwapInteractorImpl @AssistedInject constructor( .getOrElse { emptyList() } val walletCurrencyStatusesExceptInitial = walletCurrencyStatuses - .filter { - val currencyFilter = it.currency.network.backendId != currency.network.backendId || - it.currency.getContractAddress() != currency.getContractAddress() - val statusFilter = it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount - val notCustomTokenFilter = !it.currency.isCustom - statusFilter && currencyFilter && notCustomTokenFilter + .filter { status -> + val isDifferentCurrency = status.currency.network.backendId != currency.network.backendId || + status.currency.getContractAddress() != currency.getContractAddress() + val hasValidStatus = + status.value is CryptoCurrencyStatus.Loaded || status.value is CryptoCurrencyStatus.NoAccount + val isNotCustomToken = !status.currency.isCustom + hasValidStatus && isDifferentCurrency && isNotCustomToken } if (walletCurrencyStatusesExceptInitial.isEmpty()) { @@ -169,15 +171,15 @@ internal class SwapInteractorImpl @AssistedInject constructor( val walletAccountCurrencyStatusesExceptInitial = walletAccountCurrencyStatuses .mapNotNull { accountStatus -> - val filteredCurrencies = accountStatus.flattenCurrencies().filter { - val currencyFilter = it.currency.network.backendId != currency.network.backendId || - it.currency.getContractAddress() != currency.getContractAddress() + val filteredCurrencies = accountStatus.flattenCurrencies().filter { status -> + val isDifferentCurrency = status.currency.network.backendId != currency.network.backendId || + status.currency.getContractAddress() != currency.getContractAddress() - val statusFilter = - it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount - val notCustomTokenFilter = !it.currency.isCustom + val hasValidStatus = + status.value is CryptoCurrencyStatus.Loaded || status.value is CryptoCurrencyStatus.NoAccount + val isNotCustomToken = !status.currency.isCustom - statusFilter && currencyFilter && notCustomTokenFilter + hasValidStatus && isDifferentCurrency && isNotCustomToken } if (filteredCurrencies.isNotEmpty()) { @@ -228,9 +230,9 @@ internal class SwapInteractorImpl @AssistedInject constructor( tokenInfoForFilter: (SwapPairLeast) -> LeastTokenInfo, tokenInfoForAvailable: (SwapPairLeast) -> LeastTokenInfo, ): CurrenciesGroup { - val filteredPairs = leastPairs.filter { - tokenInfoForFilter(it).contractAddress == currency.getContractAddress() && - tokenInfoForFilter(it).network == currency.network.backendId + val filteredPairs = leastPairs.filter { pair -> + tokenInfoForFilter(pair).contractAddress == currency.getContractAddress() && + tokenInfoForFilter(pair).network == currency.network.backendId } val availableCryptoCurrencies = cryptoCurrenciesList.mapNotNull { cryptoCurrencyStatus -> @@ -261,16 +263,16 @@ internal class SwapInteractorImpl @AssistedInject constructor( tokenInfoForFilter: (SwapPairLeast) -> LeastTokenInfo, tokenInfoForAvailable: (SwapPairLeast) -> LeastTokenInfo, ): CurrenciesGroup { - val filteredPairs = leastPairs.filter { - tokenInfoForFilter(it).contractAddress == currency.getContractAddress() && - tokenInfoForFilter(it).network == currency.network.backendId + val filteredPairs = leastPairs.filter { pair -> + tokenInfoForFilter(pair).contractAddress == currency.getContractAddress() && + tokenInfoForFilter(pair).network == currency.network.backendId } - val accountCurrencyList = cryptoCurrenciesList.mapNotNull { (account, currencyStatusList) -> - val account = account as? Account.CryptoPortfolio ?: return@mapNotNull null + val accountCurrencyList = cryptoCurrenciesList.mapNotNull { (accountEntry, currencyStatusList) -> + val cryptoPortfolio = accountEntry as? Account.CryptoPortfolio ?: return@mapNotNull null AccountSwapAvailability( - account = account, + account = cryptoPortfolio, currencyList = currencyStatusList.map { currencyStatus -> val providers = findProvidersForPair( cryptoCurrencyStatuses = currencyStatus, @@ -280,7 +282,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val isUnavailable = providers.isNullOrEmpty() AccountSwapCurrency( isAvailable = !isUnavailable, - account = account, + account = cryptoPortfolio, cryptoCurrencyStatus = currencyStatus, providers = providers.orEmpty(), ) @@ -304,13 +306,13 @@ internal class SwapInteractorImpl @AssistedInject constructor( val requirements = getAssetRequirementsUseCase.invoke(userWalletId, cryptoCurrencyStatuses.currency).getOrNull() val isAvailableForSwap = rampStateManager.checkAssetRequirements(requirements) - return swapPairsLeastList.firstNotNullOfOrNull { - val listTokenInfo = tokenInfoForAvailable(it) + return swapPairsLeastList.firstNotNullOfOrNull { pair -> + val listTokenInfo = tokenInfoForAvailable(pair) if (cryptoCurrencyStatuses.currency.network.backendId == listTokenInfo.network && cryptoCurrencyStatuses.currency.getContractAddress() == listTokenInfo.contractAddress && isAvailableForSwap ) { - it.providers + pair.providers } else { null } @@ -351,8 +353,8 @@ internal class SwapInteractorImpl @AssistedInject constructor( amount = amount?.value, contractAddress = permissionOptions.forTokenContractAddress, spenderAddress = permissionOptions.spenderAddress, - ).getOrElse { - Timber.e(it, "Failed to create approveTransaction") + ).getOrElse { error -> + Timber.e(error, "Failed to create approveTransaction") return SwapTransactionState.Error.UnknownError } @@ -450,6 +452,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( }.toMap() } + @Suppress("LongMethod") private suspend fun manageDex( networkId: String, fromToken: CryptoCurrencyStatus, @@ -487,8 +490,13 @@ internal class SwapInteractorImpl @AssistedInject constructor( val fromTokenAddress = getTokenAddress(fromToken.currency) val isAllowedToSpend = maybeQuotes.fold( ifRight = { quotes -> - quotes.allowanceContract?.let { - isAllowedToSpend(networkId, fromToken.currency, amount, it) + quotes.allowanceContract?.let { allowanceContract -> + isAllowedToSpend( + networkId = networkId, + fromToken = fromToken.currency, + amount = amount, + spenderAddress = allowanceContract, + ) } != false }, ifLeft = { false }, @@ -626,7 +634,12 @@ internal class SwapInteractorImpl @AssistedInject constructor( is TxFeeState.MultipleFeeState -> feeState.getFeeByType(selectedFee).feeValue is TxFeeState.SingleFeeState -> feeState.fee.feeValue } - val balanceAfterTransaction = getCoinBalanceAfterTransaction(fromTokenStatus, amount, includeFeeInAmount, fee) + val balanceAfterTransaction = getCoinBalanceAfterTransaction( + fromTokenStatus = fromTokenStatus, + amount = amount, + includeFeeInAmount = includeFeeInAmount, + fee = fee, + ) val amountToRequest = if (includeFeeInAmount is IncludeFeeInAmount.Included) { includeFeeInAmount.amountSubtractFee } else { @@ -866,8 +879,8 @@ internal class SwapInteractorImpl @AssistedInject constructor( userWalletId = userWalletId, network = currencyToSendStatus.currency.network, txExtras = createDexTxExtras(dataToSign, currencyToSendStatus.currency.network, txFee.fee.getGasLimit()), - ).getOrElse { - Timber.e(it, "Failed to create swap dex tx data") + ).getOrElse { error -> + Timber.e(error, "Failed to create swap dex tx data") return SwapTransactionState.Error.UnknownError } return handleSwapResult( @@ -938,11 +951,13 @@ internal class SwapInteractorImpl @AssistedInject constructor( ) return result.fold( ifRight = { txHash -> + val networkAddress = currencyToSendStatus.value.networkAddress + val fromAddress = networkAddress?.defaultAddress?.value.orEmpty() repository.exchangeSent( userWallet = userWallet, txId = swapData.transaction.txId, fromNetwork = currencyToSendStatus.currency.network.backendId, - fromAddress = currencyToSendStatus.value.networkAddress?.defaultAddress?.value.orEmpty(), + fromAddress = fromAddress, payInAddress = payInAddress, txHash = txHash, payInExtraId = swapData.transaction.txExtraId, @@ -1000,12 +1015,16 @@ internal class SwapInteractorImpl @AssistedInject constructor( expressOperationType: ExpressOperationType, isTangemPayWithdrawal: Boolean, ): SwapTransactionState { + val fromNetworkAddress = currencyToSend.value.networkAddress + val fromAddress = fromNetworkAddress?.defaultAddress?.value.orEmpty() + val toNetworkAddress = currencyToGet.value.networkAddress + val toAddress = toNetworkAddress?.defaultAddress?.value.orEmpty() val exchangeData = repository.getExchangeData( userWallet = userWallet, fromContractAddress = currencyToSend.currency.getContractAddress(), fromNetwork = currencyToSend.currency.network.backendId, toContractAddress = currencyToGet.currency.getContractAddress(), - fromAddress = currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty(), + fromAddress = fromAddress, toNetwork = currencyToGet.currency.network.backendId, fromAmount = amount.toStringWithRightOffset(), fromDecimals = amount.decimals, @@ -1013,10 +1032,10 @@ internal class SwapInteractorImpl @AssistedInject constructor( providerId = swapProvider.providerId, rateType = RateType.FLOAT, expressOperationType = expressOperationType, - toAddress = currencyToGet.value.networkAddress?.defaultAddress?.value.orEmpty(), + toAddress = toAddress, refundAddress = currencyToSend.value.networkAddress?.defaultAddress?.value, refundExtraId = null, // currently always null, - ).getOrElse { return SwapTransactionState.Error.ExpressError(it) } + ).getOrElse { error -> return SwapTransactionState.Error.ExpressError(error) } val exchangeDataCex = exchangeData.transaction as? ExpressTransactionModel.CEX ?: return SwapTransactionState.Error.UnknownError @@ -1063,8 +1082,8 @@ internal class SwapInteractorImpl @AssistedInject constructor( destination = exchangeDataCex.txTo, userWalletId = userWalletId, network = currencyToSend.currency.network, - ).getOrElse { - Timber.e(it, "Failed to create swap CEX tx data") + ).getOrElse { error -> + Timber.e(error, "Failed to create swap CEX tx data") return SwapTransactionState.Error.UnknownError } @@ -1079,14 +1098,16 @@ internal class SwapInteractorImpl @AssistedInject constructor( ) val derivationPath = currencyToSend.currency.network.derivationPath.value + val cexNetworkAddress = currencyToSend.value.networkAddress + val cexFromAddress = cexNetworkAddress?.defaultAddress?.value.orEmpty() return result.fold( - ifLeft = { SwapTransactionState.Error.TransactionError(it) }, + ifLeft = { error -> SwapTransactionState.Error.TransactionError(error) }, ifRight = { txHash -> repository.exchangeSent( userWallet = userWallet, txId = exchangeDataCex.txId, fromNetwork = currencyToSend.currency.network.backendId, - fromAddress = currencyToSend.value.networkAddress?.defaultAddress?.value.orEmpty(), + fromAddress = cexFromAddress, payInAddress = txData.destinationAddress, txHash = txHash, payInExtraId = exchangeDataCex.txExtraId, @@ -1182,8 +1203,13 @@ internal class SwapInteractorImpl @AssistedInject constructor( isReverseFromTo: Boolean, ): CryptoCurrencyStatus? { val group = state.getGroupWithReverse(isReverseFromTo) - return initialToCurrencyResolver.tryGetFromCache(userWallet, initialCryptoCurrency, state, isReverseFromTo) - ?: initialToCurrencyResolver.tryGetWithMaxAmount(state, isReverseFromTo) + return initialToCurrencyResolver.tryGetFromCache( + userWallet = userWallet, + initialCryptoCurrency = initialCryptoCurrency, + state = state, + isReverseFromTo = isReverseFromTo, + ) + ?: initialToCurrencyResolver.tryGetWithMaxAmount(state = state, isReverseFromTo = isReverseFromTo) ?: group.available.firstOrNull()?.currencyStatus } @@ -1193,9 +1219,18 @@ internal class SwapInteractorImpl @AssistedInject constructor( isReverseFromTo: Boolean, ): AccountSwapCurrency? { val group = state.getGroupWithReverse(isReverseFromTo) - return initialToCurrencyResolver.tryGetFromCacheV2(userWallet, initialCryptoCurrency, state, isReverseFromTo) - ?: initialToCurrencyResolver.tryGetWithMaxAmountV2(state, isReverseFromTo) - ?: group.accountCurrencyList.firstNotNullOfOrNull { it.currencyList.firstOrNull { it.isAvailable } } + return initialToCurrencyResolver.tryGetFromCacheV2( + userWallet = userWallet, + initialCryptoCurrency = initialCryptoCurrency, + state = state, + isReverseFromTo = isReverseFromTo, + ) + ?: initialToCurrencyResolver.tryGetWithMaxAmountV2(state = state, isReverseFromTo = isReverseFromTo) + ?: group.accountCurrencyList.firstNotNullOfOrNull { accountSwapAvailability -> + accountSwapAvailability.currencyList.firstOrNull { accountSwapCurrency -> + accountSwapCurrency.isAvailable + } + } } override fun getNativeToken(networkId: String): CryptoCurrency { @@ -1367,7 +1402,9 @@ internal class SwapInteractorImpl @AssistedInject constructor( swapAmount = amount, quotesLoadedState = swapState, isAllowedToSpend = isAllowedToSpend, - spenderAddress = quoteModel.allowanceContract, + spenderAddress = requireNotNull(quoteModel.allowanceContract) { + "allowanceContract is required for DEX" + }, ) if (state !is SwapState.QuotesLoadedState) return state state.copy( @@ -1548,19 +1585,23 @@ internal class SwapInteractorImpl @AssistedInject constructor( selectedFee: FeeType, expressOperationType: ExpressOperationType, ): SwapState { + val fromNetworkAddress = fromToken.value.networkAddress + val dexFromAddress = fromNetworkAddress?.defaultAddress?.value.orEmpty() + val toNetworkAddress = toToken.value.networkAddress + val dexToAddress = toNetworkAddress?.defaultAddress?.value.orEmpty() return repository.getExchangeData( userWallet = userWallet, fromContractAddress = fromToken.currency.getContractAddress(), fromNetwork = fromToken.currency.network.backendId, toContractAddress = toToken.currency.getContractAddress(), - fromAddress = fromToken.value.networkAddress?.defaultAddress?.value.orEmpty(), + fromAddress = dexFromAddress, toNetwork = toToken.currency.network.backendId, fromAmount = amount.toStringWithRightOffset(), fromDecimals = amount.decimals, toDecimals = toToken.currency.decimals, providerId = provider.providerId, rateType = RateType.FLOAT, - toAddress = toToken.value.networkAddress?.defaultAddress?.value.orEmpty(), + toAddress = dexToAddress, refundAddress = fromToken.value.networkAddress?.defaultAddress?.value, expressOperationType = expressOperationType, ).fold( @@ -1813,7 +1854,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( fromAccount: Account.CryptoPortfolio?, swapAmount: SwapAmount, quotesLoadedState: SwapState.QuotesLoadedState, - spenderAddress: String?, + spenderAddress: String, isAllowedToSpend: Boolean, ): SwapState { val fromToken = fromTokenStatus.currency @@ -1837,7 +1878,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val derivationPath = fromToken.network.derivationPath.value // setting up amount for approve with given amount for swap [SwapApproveType.Limited] val callData = SmartContractCallDataProviderFactory.getApprovalCallData( - spenderAddress = requireNotNull(spenderAddress) { "Spender address is null" }, + spenderAddress = spenderAddress, amount = swapAmount.value.convertToSdkAmount(fromTokenStatus), blockchain = fromToken.network.toBlockchain(), ) @@ -2212,9 +2253,9 @@ internal class SwapInteractorImpl @AssistedInject constructor( val token = tokens .filterIsInstance() - .find { - it.contractAddress.equals(feePaidCurrency.contractAddress, ignoreCase = true) && - it.network.derivationPath == fromTokenStatus.currency.network.derivationPath + .find { cryptoToken -> + cryptoToken.contractAddress.equals(feePaidCurrency.contractAddress, ignoreCase = true) && + cryptoToken.network.derivationPath == fromTokenStatus.currency.network.derivationPath } SwapFeeState.NotEnough( @@ -2268,18 +2309,18 @@ internal class SwapInteractorImpl @AssistedInject constructor( } private suspend fun Set.getQuotesOrEmpty(): Set { - return runCatching { - val cachedQuotes = quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) + return runSuspendCatching { + val cachedQuotes = quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this@getQuotesOrEmpty) - val allQuotesFound = cachedQuotes?.all { it.value !is QuoteStatus.Empty } == true + val areAllQuotesFound = cachedQuotes?.all { quote -> quote.value !is QuoteStatus.Empty } == true - if (allQuotesFound) return@runCatching cachedQuotes + if (areAllQuotesFound) return@runSuspendCatching cachedQuotes.orEmpty() val currenciesIds = if (cachedQuotes.isNullOrEmpty()) { - this + this@getQuotesOrEmpty } else { - cachedQuotes.mapNotNullTo(hashSetOf()) { - if (it.value is QuoteStatus.Empty) it.rawCurrencyId else null + cachedQuotes.mapNotNullTo(hashSetOf()) { quote -> + if (quote.value is QuoteStatus.Empty) quote.rawCurrencyId else null } } @@ -2287,10 +2328,11 @@ internal class SwapInteractorImpl @AssistedInject constructor( params = MultiQuoteStatusFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = null), ) - quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this) + quotesRepository.getMultiQuoteSyncOrNull(currenciesIds = this@getQuotesOrEmpty).orEmpty() + }.getOrElse { e -> + Timber.e(e, "Failed to get quotes: ${e.message.orEmpty()}") + emptySet() } - .getOrNull() - .orEmpty() } private fun isSolana(networkId: String): Boolean { @@ -2302,7 +2344,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( return try { SolanaTransactionHelper.removeSignaturesPlaceholders(hash) } catch (e: Exception) { - Timber.e("Failed to format the hash: ${e.message}") + Timber.e("Failed to format the hash: ${e.message.orEmpty()}") hash } } diff --git a/features/swap/impl/detekt-baseline-debug.xml b/features/swap/impl/detekt-baseline-debug.xml deleted file mode 100644 index 72441faba2..0000000000 --- a/features/swap/impl/detekt-baseline-debug.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - BooleanPropertyNaming:ChooseFeeBottomSheet.kt$val showDivider = content.feeItems.lastIndex != index - BooleanPropertyNaming:SwapEvents.kt$SwapEvents.ChooseTokenScreenOpened$val availableTokens: Boolean - BooleanPropertyNaming:SwapEvents.kt$SwapEvents.ChooseTokenScreenResult$val tokenChosen: Boolean - BooleanPropertyNaming:SwapNotificationsFactory.kt$SwapNotificationsFactory$val needShowCoverWarning = quoteModel.preparedSwapConfigState.isBalanceEnough && quoteModel.permissionState !is PermissionDataState.PermissionLoading && feeEnoughState.feeCurrency != fromToken - BooleanPropertyNaming:SwapSelectTokenStateHolder.kt$SwapSelectTokenStateHolder$val afterSearch: Boolean - BooleanPropertyNaming:SwapSelectTokenStateHolder.kt$TokenToSelectState.TokenToSelect$val available: Boolean = true - BooleanPropertyNaming:SwapStateHolder.kt$SwapButton$val enabled: Boolean - BooleanPropertyNaming:SwapStateHolder.kt$TransactionCardType.ReadOnly$val showWarning: Boolean = false - BooleanPropertyNaming:SwapSuccessStateHolder.kt$SwapSuccessStateHolder$val showStatusButton: Boolean - CastNullableToNonNullableType:SwapModel.kt$SwapModel$as - MaxChainedCallsOnSameLine:SwapModel.kt$SwapModel$it.value.toTokenInfo.cryptoCurrencyStatus.currency.decimals - MaxChainedCallsOnSameLine:SwapNotificationsFactory.kt$SwapNotificationsFactory$quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency.network.currencySymbol - MultilineLambdaItParameter:AccountTokenItemConverter.kt$AccountTokenItemConverter${ TokenItemState.TitleState.Content( text = stringReference(value = it.currency.name), isAvailable = false, ) } - MultilineLambdaItParameter:AccountTokenItemConverter.kt$AccountTokenItemConverter${ createSubtitleState( status = it, isAvailable = false, text = unavailableErrorText, ) } - MultilineLambdaItParameter:AccountTokenItemConverter.kt$AccountTokenItemConverter${ createSubtitleState( status = it, isAvailable = true, text = stringReference(value = it.currency.symbol), ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = if (it > 0) "+$it%" else "$it%", style = TangemTheme.typography.body2, color = textColor, modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), overflow = TextOverflow.Ellipsis, maxLines = 1, ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = it, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.primary1, ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = it, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = it, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = it.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.padding(top = TangemTheme.dimens.spacing6), ) } - MultilineLambdaItParameter:ProviderItem.kt${ Text( text = it.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, overflow = TextOverflow.Ellipsis, maxLines = 1, ) } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ LegalState( title = resourceReference(R.string.common_privacy_policy), link = it, onClick = actions.onLinkClick, ) } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ LegalState( title = resourceReference(R.string.common_terms_of_use), link = it, onClick = actions.onLinkClick, ) } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ it is SwapNotificationUM.Error || it is NotificationUM.Error || it is SwapNotificationUM.Warning.ExpressError || it is SwapNotificationUM.Warning.ExpressGeneralError || it is SwapNotificationUM.Warning.NoAvailableTokensToSwap || it is SwapNotificationUM.Warning.NeedReserveToCreateAccount || it is SwapNotificationUM.Info.PermissionNeeded } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ it.convertToProviderBottomSheetState( pricesLowerBest = pricesLowerBest, onProviderSelect = actions.onProviderSelect, needApplyFCARestrictions = needApplyFCARestrictions, ) } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ val selectedItem = when (it) { FeeType.NORMAL -> txFeeState.normalFee FeeType.PRIORITY -> txFeeState.priorityFee } actions.onSelectFeeType.invoke(selectedItem) } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ val tokenInfo = tokenSwapInfoForProviders[it.id] if (it is ProviderState.Content && tokenInfo != null) { val rateString = tokenInfo.tokenAmount .getFormattedCryptoAmount(tokenInfo.cryptoCurrencyStatus.currency) it.copy( subtitle = stringReference(rateString), percentLowerThenBest = pricesLowerBest[it.id]?.let { percent -> PercentDifference.Value(percent) } ?: PercentDifference.Value(0f), ) } else { it } } - MultilineLambdaItParameter:StateBuilder.kt$StateBuilder${ when (it) { is TokenToSelectState.TokenToSelect -> { it.copy( addedTokenBalanceData = it.addedTokenBalanceData?.copy(isBalanceHidden = isBalanceHidden), ) } is TokenToSelectState.Title -> { it } } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ AccountCryptoCurrencyStatus( account = it.account, status = it.cryptoCurrencyStatus, ) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ Timber.d("${coin.id} balance is ${it.value.amount}") dataState = dataState.copy( feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase( userWalletId = userWalletId, cryptoCurrencyStatus = it, ).getOrNull() ?: it, ) uiState = if (isFromCurrency) { dataState = dataState.copy(fromCryptoCurrency = it) stateBuilder.updateSendCurrencyBalance(uiState, it) } else { dataState = dataState.copy(toCryptoCurrency = it) stateBuilder.updateReceiveCurrencyBalance(uiState, it) } startLoadingQuotesFromLastState(isSilent = true) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ Timber.e("Error when loading quotes: $it") uiState = stateBuilder.addNotification(uiState, null) { startLoadingQuotesFromLastState() } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ Timber.e(it) applyInitialTokenChoice( state = TokensDataStateExpress.EMPTY, selectedCurrency = null, selectedAccount = null, isReverseFromTo = isReverseFromTo, ) uiState = stateBuilder.createInitialErrorState( uiState, (it as? ExpressException)?.expressDataError?.code ?: ExpressDataError.UnknownError.code, ) { uiState = stateBuilder.createInitialLoadingState( initialCurrencyFrom = initialCurrencyFrom, initialCurrencyTo = initialCurrencyTo, fromNetworkInfo = initialCurrencyFrom.getNetworkInfo(), ) initTokens(isReverseFromTo) } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ Timber.e(it) startLoadingQuotesFromLastState() makeDefaultAlert() } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ Timber.e(it.message.orEmpty()) makeDefaultAlert() } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ getAccountCurrencyStatusUseCase.invokeSync( userWalletId = userWalletId, currency = it, ).getOrNull() } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( userWalletId = userWalletId, cryptoCurrencyId = it.id, ).getOrNull() } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ if (!it.value.fromTokenInfo.amountFiat.isNullOrZero() && !it.value.toTokenInfo.amountFiat.isNullOrZero()) { it.value.fromTokenInfo.amountFiat.divide( it.value.toTokenInfo.amountFiat, it.value.toTokenInfo.cryptoCurrencyStatus.currency.decimals, RoundingMode.HALF_UP, ) } else { BigDecimal.ZERO } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ if (it.key != selectedProviderEntry.key) { val amount = it.value.toTokenInfo.tokenAmount.value val percentDiff = BigDecimal.ONE.minus( selectedProviderRate.divide(amount, RoundingMode.HALF_UP), ).multiply(hundredPercent) it.key.providerId to percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat() } else { null } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ isBalanceHidden = it.isBalanceHidden uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ it is SwapState.SwapError && ( it.error is ExpressDataError.ExchangeTooSmallAmountError || it.error is ExpressDataError.ExchangeTooBigAmountError ) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ it.currencyStatus.currency.name.contains(searchQuery, ignoreCase = true) || it.currencyStatus.currency.symbol.contains(searchQuery, ignoreCase = true) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ subscribeToCoinBalanceUpdates( userWalletId = userWalletId, coin = it, isFromCurrency = false, ) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ subscribeToCoinBalanceUpdates( userWalletId = userWalletId, coin = it, isFromCurrency = true, ) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ uiState = stateBuilder.dismissBottomSheet(uiState) dataState = dataState.copy(selectedFee = it) modelScope.launch(dispatchers.io) { startLoadingQuotesFromLastState(false) } } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ val balance = swapInteractor.getTokenBalance(it) onAmountChanged(balance.formatToUIRepresentation()) } - MultilineLambdaItParameter:SwapModel.kt$SwapModel${ val provider = findAndSelectProvider(it) val swapState = dataState.lastLoadedSwapStates[provider] val fromToken = dataState.fromCryptoCurrency if (provider != null && swapState != null && fromToken != null) { analyticsEventHandler.send(SwapEvents.ProviderChosen(provider)) uiState = stateBuilder.dismissBottomSheet(uiState) setupLoadedState( provider = provider, state = swapState, fromToken = fromToken, ) } } - MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, ) } - MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier .align(Alignment.CenterVertically) .testTag(SwapTokenScreenTestTags.BALANCE), ) } - MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20), ) } - NamedArguments:SwapModel.kt$SwapModel$PeriodicTask( UPDATE_DELAY, task = { uiState = stateBuilder.createSilentLoadState(uiState) runCatching(dispatchers.io) { dataState = dataState.copy( amount = amount, reduceBalanceBy = reduceBalanceBy, swapDataModel = null, approveDataModel = null, ) swapInteractor.findBestQuote( fromToken = fromToken, fromAccount = fromAccount, toToken = toToken, toAccount = toAccount, providers = toProvidersList, amountToSwap = amount, reduceBalanceBy = reduceBalanceBy, selectedFee = dataState.selectedFee?.feeType ?: FeeType.NORMAL, ) } }, onSuccess = { providersState -> if (providersState.isNotEmpty()) { val (provider, state) = updateLoadedQuotes(providersState) setupLoadedState(provider, state, fromToken) val successStates = providersState.getLastLoadedSuccessStates() val pricesLowerBest = getPricesLowerBest(provider.providerId, successStates) uiState = stateBuilder.updateProvidersBottomSheetContent( uiState = uiState, pricesLowerBest = pricesLowerBest, tokenSwapInfoForProviders = successStates.entries .associate { it.key.providerId to it.value.toTokenInfo }, ) } else { Timber.e("Accidentally empty quotes list") } }, onError = { Timber.e("Error when loading quotes: $it") uiState = stateBuilder.addNotification(uiState, null) { startLoadingQuotesFromLastState() } }, ) - NoNameShadowing:SwapModel.kt$SwapModel${ it.cryptoCurrencyStatus } - NoNameShadowing:SwapModel.kt$SwapModel${ it.cryptoCurrencyStatus.currency.id.value == id } - NoNameShadowing:SwapModel.kt$SwapModel${ it.key == selectedSwapProvider } - NullableToStringCall:SwapModel.kt$SwapModel$${currencyStatus.value.amount} - NullableToStringCall:SwapModel.kt$SwapModel$${it.value.amount} - NullableToStringCall:TransactionCard.kt$data.toString() - PropertyUsedBeforeDeclaration:SwapModel.kt$SwapModel$isAccountsMode - SuspendFunSwallowedCancellation:SwapModel.kt$SwapModel$runCatching - UnnecessaryEventHandlerParameter:ChooseFeeBottomSheet.kt$onReadMoreClick: (String) -> Unit - UnnecessaryLet:SwapModel.kt$SwapModel$let { return nonEmptyStates.entries.first { it.key == selectedSwapProvider }.toPair() } - UnusedImports:TransactionCard.kt$import com.tangem.domain.models.account.Account - UseEmptyCounterpart:StoriesEvents.kt$StoriesEvents$mapOf() - UseEmptyCounterpart:SwapEvents.kt$SwapEvents$mapOf() - UseOrEmpty:StateBuilder.kt$StateBuilder$initialCurrencyTo?.symbol ?: "" - UseOrEmpty:SwapModel.kt$SwapModel$receiveToken ?: "" - UseOrEmpty:SwapScreenContent.kt$swapCardState.tokenIconUrl ?: "" - VarCouldBeVal:SwapModel.kt$SwapModel$private var swapRouter: SwapRouter = SwapRouter(router = router) - - diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/StoriesEvents.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/StoriesEvents.kt index b7f89e9288..1174a84ccc 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/StoriesEvents.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/StoriesEvents.kt @@ -6,7 +6,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.WATCHED sealed class StoriesEvents( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent("Stories", event, params) { data class SwapStories( diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt index c1ee5ff99d..e1c2640942 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt @@ -15,7 +15,7 @@ private const val PROMO_CATEGORY = "Promo" sealed class SwapEvents( event: String, - params: Map = mapOf(), + params: Map = emptyMap(), ) : AnalyticsEvent(SWAP_CATEGORY, event, params) { data class SwapScreenOpened(val token: String) : SwapEvents( @@ -25,15 +25,15 @@ sealed class SwapEvents( class SendTokenBalanceClicked : SwapEvents(event = "Send Token Balance Clicked") - data class ChooseTokenScreenOpened(val availableTokens: Boolean) : SwapEvents( + data class ChooseTokenScreenOpened(val hasAvailableTokens: Boolean) : SwapEvents( event = "Choose Token Screen Opened", - params = mapOf("Available tokens" to if (availableTokens) "Yes" else "No"), + params = mapOf("Available tokens" to if (hasAvailableTokens) "Yes" else "No"), ) - data class ChooseTokenScreenResult(val tokenChosen: Boolean, val token: String? = null) : SwapEvents( + data class ChooseTokenScreenResult(val isTokenChosen: Boolean, val token: String? = null) : SwapEvents( event = "Choose Token Screen Result", params = buildMap { - put("Token Chosen", if (tokenChosen) "Yes" else "No") + put("Token Chosen", if (isTokenChosen) "Yes" else "No") token?.let { put("Token", it) } }, ) diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/AccountTokenItemConverter.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/AccountTokenItemConverter.kt index f556cdf5e5..4315d780ac 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/AccountTokenItemConverter.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/AccountTokenItemConverter.kt @@ -45,11 +45,11 @@ internal class AccountTokenItemConverter( fun createAvailableItemConverter(): TokenItemStateConverter { return TokenItemStateConverter( appCurrency = appCurrency, - subtitleStateProvider = { + subtitleStateProvider = { status -> createSubtitleState( - status = it, + status = status, isAvailable = true, - text = stringReference(value = it.currency.symbol), + text = stringReference(value = status.currency.symbol), ) }, subtitle2StateProvider = ::createSubtitle2State, @@ -64,15 +64,15 @@ internal class AccountTokenItemConverter( return TokenItemStateConverter( appCurrency = appCurrency, iconStateProvider = { CryptoCurrencyToIconStateConverter(isAvailable = false).convert(it) }, - titleStateProvider = { + titleStateProvider = { status -> TokenItemState.TitleState.Content( - text = stringReference(value = it.currency.name), + text = stringReference(value = status.currency.name), isAvailable = false, ) }, - subtitleStateProvider = { + subtitleStateProvider = { status -> createSubtitleState( - status = it, + status = status, isAvailable = false, text = unavailableErrorText, ) diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index e9f9ec7e68..ba936978ec 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -58,7 +58,7 @@ internal class TokensDataConverter( onSearchEntered = onSearchEntered, onTokenSelected = onTokenSelected, isBalanceHidden = isBalanceHiddenProvider(), - afterSearch = group.isAfterSearch, + isAfterSearch = group.isAfterSearch, ) } @@ -71,7 +71,7 @@ internal class TokensDataConverter( id = cryptoCurrencyStatus.currency.id.value, name = cryptoCurrencyStatus.currency.name, symbol = cryptoCurrencyStatus.currency.symbol, - available = isAvailable, + isAvailable = isAvailable, tokenIcon = convertIcon(cryptoCurrencyStatus.currency, isAvailable), addedTokenBalanceData = TokenBalanceData( amount = formatCryptoAmount(cryptoCurrencyStatus), diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverterV2.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverterV2.kt index 3dbd07cda3..90dd691aaa 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverterV2.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/converters/TokensDataConverterV2.kt @@ -60,7 +60,7 @@ internal class TokensDataConverterV2( onSearchEntered = onSearchEntered, onTokenSelected = onTokenSelected, isBalanceHidden = isBalanceHidden, - afterSearch = tokensDataState.isAfterSearch, + isAfterSearch = tokensDataState.isAfterSearch, ), ) } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index d5884d3b72..4cdd1f15ff 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -139,6 +139,7 @@ internal class SwapModel @Inject constructor( private var initialToStatus: CryptoCurrencyStatus? = null private var isBalanceHidden = true + private var isAccountsMode: Boolean = false private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() @@ -151,7 +152,10 @@ internal class SwapModel @Inject constructor( ) private val inputNumberFormatter = - InputNumberFormatter(NumberFormat.getInstance(Locale.getDefault()) as DecimalFormat) + InputNumberFormatter( + NumberFormat.getInstance(Locale.getDefault()) as? DecimalFormat + ?: error("NumberFormat is not DecimalFormat"), + ) private val amountDebouncer = Debouncer() private val singleTaskScheduler = SingleTaskScheduler>() @@ -170,18 +174,17 @@ internal class SwapModel @Inject constructor( private var isOrderReversed = false private val lastAmount = mutableStateOf(INITIAL_AMOUNT) private val lastReducedBalanceBy = mutableStateOf(BigDecimal.ZERO) - private var swapRouter: SwapRouter = SwapRouter(router = router) + private val swapRouter: SwapRouter = SwapRouter(router = router) private var userCountry: UserCountry? = null private lateinit var fromAccountCurrencyStatus: AccountCryptoCurrencyStatus private var toAccountCurrencyStatus: AccountCryptoCurrencyStatus? = null - private var isAccountsMode: Boolean = false - private val isUserResolvableError: (SwapState) -> Boolean = { - it is SwapState.SwapError && + private val isUserResolvableError: (SwapState) -> Boolean = { swapState -> + swapState is SwapState.SwapError && ( - it.error is ExpressDataError.ExchangeTooSmallAmountError || - it.error is ExpressDataError.ExchangeTooBigAmountError + swapState.error is ExpressDataError.ExchangeTooSmallAmountError || + swapState.error is ExpressDataError.ExchangeTooBigAmountError ) } @@ -209,10 +212,10 @@ internal class SwapModel @Inject constructor( userWalletId = userWalletId, currency = initialCurrencyFrom, ).getOrNull() - val toAccountStatus = initialCurrencyTo?.let { + val toAccountStatus = initialCurrencyTo?.let { currencyTo -> getAccountCurrencyStatusUseCase.invokeSync( userWalletId = userWalletId, - currency = it, + currency = currencyTo, ).getOrNull() } @@ -227,10 +230,10 @@ internal class SwapModel @Inject constructor( } } else { val fromStatus = getFromStatus() - val toStatus = initialCurrencyTo?.let { + val toStatus = initialCurrencyTo?.let { currencyTo -> getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( userWalletId = userWalletId, - cryptoCurrencyId = it.id, + cryptoCurrencyId = currencyTo.id, ).getOrNull() } @@ -247,8 +250,8 @@ internal class SwapModel @Inject constructor( analyticsEventHandler.send(SwapEvents.SwapScreenOpened(initialCurrencyFrom.symbol)) getBalanceHidingSettingsUseCase() - .onEach { - isBalanceHidden = it.isBalanceHidden + .onEach { settings -> + isBalanceHidden = settings.isBalanceHidden uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden) } .launchIn(modelScope) @@ -274,7 +277,7 @@ internal class SwapModel @Inject constructor( val isAnyAvailableAccountTokensFrom = !dataState.tokensDataState?.fromGroup?.accountCurrencyList.isNullOrEmpty() val isAnyAvailableTokens = isAnyAvailableTokensTo || isAnyAvailableTokensFrom || isAnyAvailableAccountTokensTo || isAnyAvailableAccountTokensFrom - analyticsEventHandler.send(SwapEvents.ChooseTokenScreenOpened(availableTokens = isAnyAvailableTokens)) + analyticsEventHandler.send(SwapEvents.ChooseTokenScreenOpened(hasAvailableTokens = isAnyAvailableTokens)) } private fun initTokens(isReverseFromTo: Boolean) { @@ -289,10 +292,10 @@ internal class SwapModel @Inject constructor( initialCryptoCurrency = initialCurrencyFrom, state = state, isReverseFromTo = isReverseFromTo, - )?.let { + )?.let { accountSwapCurrency -> AccountCryptoCurrencyStatus( - account = it.account, - status = it.cryptoCurrencyStatus, + account = accountSwapCurrency.account, + status = accountSwapCurrency.cryptoCurrencyStatus, ) } selectedAccountCurrency?.status to selectedAccountCurrency?.account @@ -312,23 +315,23 @@ internal class SwapModel @Inject constructor( isReverseFromTo = isReverseFromTo, ) - (dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let { + (dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let { coin -> subscribeToCoinBalanceUpdates( userWalletId = userWalletId, - coin = it, + coin = coin, isFromCurrency = true, ) } - (dataState.toCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let { + (dataState.toCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let { coin -> subscribeToCoinBalanceUpdates( userWalletId = userWalletId, - coin = it, + coin = coin, isFromCurrency = false, ) } - }.onFailure { - Timber.e(it) + }.onFailure { error -> + Timber.e(error) applyInitialTokenChoice( state = TokensDataStateExpress.EMPTY, @@ -339,7 +342,7 @@ internal class SwapModel @Inject constructor( uiState = stateBuilder.createInitialErrorState( uiState, - (it as? ExpressException)?.expressDataError?.code ?: ExpressDataError.UnknownError.code, + (error as? ExpressException)?.expressDataError?.code ?: ExpressDataError.UnknownError.code, ) { uiState = stateBuilder.createInitialLoadingState( initialCurrencyFrom = initialCurrencyFrom, @@ -496,7 +499,7 @@ internal class SwapModel @Inject constructor( toProvidersList: List, ): PeriodicTask> { return PeriodicTask( - UPDATE_DELAY, + delay = UPDATE_DELAY, task = { uiState = stateBuilder.createSilentLoadState(uiState) runCatching(dispatchers.io) { @@ -534,8 +537,8 @@ internal class SwapModel @Inject constructor( Timber.e("Accidentally empty quotes list") } }, - onError = { - Timber.e("Error when loading quotes: $it") + onError = { error -> + Timber.e("Error when loading quotes: $error") uiState = stateBuilder.addNotification(uiState, null) { startLoadingQuotesFromLastState() } }, ) @@ -599,13 +602,13 @@ internal class SwapModel @Inject constructor( } private fun sendErrorAnalyticsEvent(error: ExpressDataError, provider: SwapProvider) { - val receiveToken = dataState.toCryptoCurrency?.currency?.let { - "${it.network.backendId}:${it.symbol}" + val receiveToken = dataState.toCryptoCurrency?.currency?.let { currency -> + "${currency.network.backendId}:${currency.symbol}" } analyticsErrorEventHandler.sendErrorEvent( SwapEvents.NoticeProviderError( sendToken = "${initialCurrencyFrom.network.backendId}:${initialCurrencyFrom.symbol}", - receiveToken = receiveToken ?: "", + receiveToken = receiveToken.orEmpty(), provider = provider, errorCode = error.code, errorMessage = error.message, @@ -614,7 +617,7 @@ internal class SwapModel @Inject constructor( } private fun updateLoadedQuotes(state: Map): Pair { - val nonEmptyStates = state.filter { it.value !is SwapState.EmptyAmountState } + val nonEmptyStates = state.filter { entry -> entry.value !is SwapState.EmptyAmountState } val selectedSwapProvider = if (nonEmptyStates.isNotEmpty()) { selectProvider(state) } else { @@ -624,8 +627,8 @@ internal class SwapModel @Inject constructor( selectedProvider = selectedSwapProvider, lastLoadedSwapStates = state, ) - selectedSwapProvider?.let { - return nonEmptyStates.entries.first { it.key == selectedSwapProvider }.toPair() + if (selectedSwapProvider != null) { + return nonEmptyStates.entries.first { entry -> entry.key == selectedSwapProvider }.toPair() } return state.entries.first().toPair() } @@ -790,8 +793,8 @@ internal class SwapModel @Inject constructor( processTangemPayWithdrawal(swapTransactionState = swapTransactionState) } } - }.onFailure { - Timber.e(it) + }.onFailure { error -> + Timber.e(error) startLoadingQuotesFromLastState() makeDefaultAlert() } @@ -867,7 +870,7 @@ internal class SwapModel @Inject constructor( @Suppress("LongMethod") private fun givePermissionsToSwap() { modelScope.launch(dispatchers.main) { - runCatching { + runSuspendCatching { val fromCryptoCurrency = requireNotNull(dataState.fromCryptoCurrency) { "dataState.fromCryptoCurrency might not be null" } @@ -884,7 +887,7 @@ internal class SwapModel @Inject constructor( TxFeeState.Empty -> { makeDefaultAlert(resourceReference(R.string.swapping_fee_estimation_error_text)) Timber.e("Fee should not be Empty") - return@runCatching + return@launch } is TxFeeState.MultipleFeeState -> fee.priorityFee is TxFeeState.SingleFeeState -> fee.fee @@ -931,8 +934,8 @@ internal class SwapModel @Inject constructor( } } }.onFailure { makeDefaultAlert() } - }.onFailure { - Timber.e(it.message.orEmpty()) + }.onFailure { error -> + Timber.e(error.message.orEmpty()) makeDefaultAlert() } } @@ -947,13 +950,13 @@ internal class SwapModel @Inject constructor( tokenDataState.toGroup } - val available = group.available.filter { - it.currencyStatus.currency.name.contains(searchQuery, ignoreCase = true) || - it.currencyStatus.currency.symbol.contains(searchQuery, ignoreCase = true) + val available = group.available.filter { swapAvailability -> + swapAvailability.currencyStatus.currency.name.contains(searchQuery, ignoreCase = true) || + swapAvailability.currencyStatus.currency.symbol.contains(searchQuery, ignoreCase = true) } - val unavailable = group.unavailable.filter { - it.currencyStatus.currency.name.contains(searchQuery, ignoreCase = true) || - it.currencyStatus.currency.symbol.contains(searchQuery, ignoreCase = true) + val unavailable = group.unavailable.filter { swapAvailability -> + swapAvailability.currencyStatus.currency.name.contains(searchQuery, ignoreCase = true) || + swapAvailability.currencyStatus.currency.symbol.contains(searchQuery, ignoreCase = true) } val accountCurrencyList = group.accountCurrencyList.mapNotNull { accountSwapAvailability -> val filteredCurrencies = accountSwapAvailability.currencyList.filter { accountSwapCurrency -> @@ -998,8 +1001,8 @@ internal class SwapModel @Inject constructor( val tokens = dataState.tokensDataState ?: return val (foundToken, foundAccount) = getSelectedTokenAndAccount(tokens, id) - foundToken?.currency?.symbol?.let { - analyticsEventHandler.send(SwapEvents.ChooseTokenScreenResult(tokenChosen = true, token = it)) + foundToken?.currency?.symbol?.let { symbol -> + analyticsEventHandler.send(SwapEvents.ChooseTokenScreenResult(isTokenChosen = true, token = symbol)) } if (foundToken != null) { @@ -1079,8 +1082,10 @@ internal class SwapModel @Inject constructor( tokens.fromGroup } else { tokens.toGroup - }.accountCurrencyList.firstNotNullOfOrNull { - it.currencyList.firstOrNull { it.cryptoCurrencyStatus.currency.id.value == id } + }.accountCurrencyList.firstNotNullOfOrNull { accountSwapAvailability -> + accountSwapAvailability.currencyList.firstOrNull { accountSwapCurrency -> + accountSwapCurrency.cryptoCurrencyStatus.currency.id.value == id + } } accountCryptoCurrencyStatus?.cryptoCurrencyStatus to accountCryptoCurrencyStatus?.account } else { @@ -1088,7 +1093,9 @@ internal class SwapModel @Inject constructor( tokens.fromGroup } else { tokens.toGroup - }.available.firstOrNull { it.currencyStatus.currency.id.value == id }?.currencyStatus to null + }.available.firstOrNull { swapAvailability -> + swapAvailability.currencyStatus.currency.id.value == id + }?.currencyStatus to null } } @@ -1105,7 +1112,7 @@ internal class SwapModel @Inject constructor( currency = coin, ).distinctUntilChanged { old, new -> old.status.value.amount == new.status.value.amount } // Check only balance changes .onEach { (account, currencyStatus) -> - Timber.d("${coin.id} balance is ${currencyStatus.value.amount}") + Timber.d("${coin.id} balance is ${currencyStatus.value.amount ?: "null"}") dataState = dataState.copy( feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase( @@ -1135,24 +1142,24 @@ internal class SwapModel @Inject constructor( userWalletId = userWalletId, currencyId = coin.id, isSingleWalletWithTokens = false, - ).mapNotNull { (it as? Either.Right)?.value } + ).mapNotNull { either -> (either as? Either.Right)?.value } .distinctUntilChanged { old, new -> old.value.amount == new.value.amount } // Check only balance changes - .onEach { - Timber.d("${coin.id} balance is ${it.value.amount}") + .onEach { status -> + Timber.d("${coin.id} balance is ${status.value.amount ?: "null"}") dataState = dataState.copy( feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase( userWalletId = userWalletId, - cryptoCurrencyStatus = it, - ).getOrNull() ?: it, + cryptoCurrencyStatus = status, + ).getOrNull() ?: status, ) uiState = if (isFromCurrency) { - dataState = dataState.copy(fromCryptoCurrency = it) - stateBuilder.updateSendCurrencyBalance(uiState, it) + dataState = dataState.copy(fromCryptoCurrency = status) + stateBuilder.updateSendCurrencyBalance(uiState, status) } else { - dataState = dataState.copy(toCryptoCurrency = it) - stateBuilder.updateReceiveCurrencyBalance(uiState, it) + dataState = dataState.copy(toCryptoCurrency = status) + stateBuilder.updateReceiveCurrencyBalance(uiState, status) } startLoadingQuotesFromLastState(isSilent = true) @@ -1179,8 +1186,8 @@ internal class SwapModel @Inject constructor( toAccount = newToAccount, ) isOrderReversed = !isOrderReversed - dataState.tokensDataState?.let { - updateTokensState(it) + dataState.tokensDataState?.let { tokensDataState -> + updateTokensState(tokensDataState) } val minTxAmount = getMinimumTransactionAmountSyncUseCase( @@ -1259,8 +1266,8 @@ internal class SwapModel @Inject constructor( } private fun onMaxAmountClicked() { - dataState.fromCryptoCurrency?.let { - val balance = swapInteractor.getTokenBalance(it) + dataState.fromCryptoCurrency?.let { fromCurrency -> + val balance = swapInteractor.getTokenBalance(fromCurrency) onAmountChanged(balance.formatToUIRepresentation()) } } @@ -1324,7 +1331,7 @@ internal class SwapModel @Inject constructor( uiState = stateBuilder.dismissBottomSheet(uiState) } else { if (swapRouter.currentScreen == SwapNavScreen.SelectToken) { - analyticsEventHandler.send(SwapEvents.ChooseTokenScreenResult(tokenChosen = false)) + analyticsEventHandler.send(SwapEvents.ChooseTokenScreenResult(isTokenChosen = false)) } swapRouter.back() } @@ -1358,9 +1365,9 @@ internal class SwapModel @Inject constructor( uiState = stateBuilder.dismissBottomSheet(uiState) } }, - onSelectFeeType = { + onSelectFeeType = { feeType -> uiState = stateBuilder.dismissBottomSheet(uiState) - dataState = dataState.copy(selectedFee = it) + dataState = dataState.copy(selectedFee = feeType) modelScope.launch(dispatchers.io) { startLoadingQuotesFromLastState(false) } @@ -1377,8 +1384,8 @@ internal class SwapModel @Inject constructor( needApplyFCARestrictions = userCountry.needApplyFCARestrictions(), ) { uiState = stateBuilder.dismissBottomSheet(uiState) } }, - onProviderSelect = { - val provider = findAndSelectProvider(it) + onProviderSelect = { providerId -> + val provider = findAndSelectProvider(providerId) val swapState = dataState.lastLoadedSwapStates[provider] val fromToken = dataState.fromCryptoCurrency if (provider != null && swapState != null && fromToken != null) { @@ -1478,11 +1485,14 @@ internal class SwapModel @Inject constructor( private fun findBestQuoteProvider(state: SuccessLoadedSwapData): SwapProvider? { // finding best quotes - return state.minByOrNull { - if (!it.value.fromTokenInfo.amountFiat.isNullOrZero() && !it.value.toTokenInfo.amountFiat.isNullOrZero()) { - it.value.fromTokenInfo.amountFiat.divide( - it.value.toTokenInfo.amountFiat, - it.value.toTokenInfo.cryptoCurrencyStatus.currency.decimals, + return state.minByOrNull { entry -> + val toTokenInfo = entry.value.toTokenInfo + val fromAmountFiat = entry.value.fromTokenInfo.amountFiat + val toAmountFiat = toTokenInfo.amountFiat + if (!fromAmountFiat.isNullOrZero() && !toAmountFiat.isNullOrZero()) { + fromAmountFiat.divide( + toAmountFiat, + toTokenInfo.cryptoCurrencyStatus.currency.decimals, RoundingMode.HALF_UP, ) } else { @@ -1492,17 +1502,19 @@ internal class SwapModel @Inject constructor( } private fun getPricesLowerBest(selectedProviderId: String, state: SuccessLoadedSwapData): Map { - val selectedProviderEntry = state.filter { it.key.providerId == selectedProviderId }.entries.firstOrNull() - ?: return emptyMap() + val selectedProviderEntry = state + .filter { entry -> entry.key.providerId == selectedProviderId } + .entries + .firstOrNull() ?: return emptyMap() val selectedProviderRate = selectedProviderEntry.value.toTokenInfo.tokenAmount.value val hundredPercent = BigDecimal("100") - return state.entries.mapNotNull { - if (it.key != selectedProviderEntry.key) { - val amount = it.value.toTokenInfo.tokenAmount.value + return state.entries.mapNotNull { entry -> + if (entry.key != selectedProviderEntry.key) { + val amount = entry.value.toTokenInfo.tokenAmount.value val percentDiff = BigDecimal.ONE.minus( selectedProviderRate.divide(amount, RoundingMode.HALF_UP), ).multiply(hundredPercent) - it.key.providerId to percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat() + entry.key.providerId to percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat() } else { null } @@ -1534,11 +1546,15 @@ internal class SwapModel @Inject constructor( return if (accountsFeatureToggles.isFeatureEnabled) { groupToFind.accountCurrencyList.firstNotNullOfOrNull { (_, currencyList) -> - currencyList.find { idToFind == it.cryptoCurrencyStatus.currency.id.value && it.isAvailable } + currencyList.find { accountSwapCurrency -> + idToFind == accountSwapCurrency.cryptoCurrencyStatus.currency.id.value && + accountSwapCurrency.isAvailable + } }?.providers } else { - groupToFind.available.find { idToFind == it.currencyStatus.currency.id.value } - ?.providers + groupToFind.available.find { swapAvailability -> + idToFind == swapAvailability.currencyStatus.currency.id.value + }?.providers } ?.filterForTangemPayWithdrawal() .orEmpty() @@ -1553,13 +1569,13 @@ internal class SwapModel @Inject constructor( } private fun Map.getLastLoadedSuccessStates(): SuccessLoadedSwapData { - return this.filter { it.value is SwapState.QuotesLoadedState } - .mapValues { it.value as SwapState.QuotesLoadedState } + return this.filter { entry -> entry.value is SwapState.QuotesLoadedState } + .mapValues { entry -> entry.value as SwapState.QuotesLoadedState } } private fun Map.consideredProvidersStates(): Map { - return this.filter { - it.value is SwapState.QuotesLoadedState || isUserResolvableError(it.value) + return this.filter { entry -> + entry.value is SwapState.QuotesLoadedState || isUserResolvableError(entry.value) } } @@ -1577,10 +1593,14 @@ internal class SwapModel @Inject constructor( val chosen = if (isOrderReversed) from else to return if (accountsFeatureToggles.isFeatureEnabled) { - currenciesGroup.accountCurrencyList.flatMap { it.currencyList.map { it.cryptoCurrencyStatus } } + currenciesGroup.accountCurrencyList.flatMap { accountSwapAvailability -> + accountSwapAvailability.currencyList.map { accountSwapCurrency -> + accountSwapCurrency.cryptoCurrencyStatus + } + } } else { - currenciesGroup.available.map { it.currencyStatus } - }.map { it.currency }.contains(chosen.currency) + currenciesGroup.available.map { swapAvailability -> swapAvailability.currencyStatus } + }.map { currencyStatus -> currencyStatus.currency }.contains(chosen.currency) } private fun sendPermissionApproveClickedEvent() { diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt index 9c60150644..731e036f94 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapNotificationsFactory.kt @@ -125,9 +125,10 @@ internal class SwapNotificationsFactory( if (quoteModel.permissionState is PermissionDataState.PermissionLoading) { add(SwapNotificationUM.Error.ApprovalInProgressWarning) } else if (quoteModel.preparedSwapConfigState.hasOutgoingTransaction) { + val fromCurrency = quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency add( SwapNotificationUM.Error.TransactionInProgressWarning( - currencySymbol = quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency.network.currencySymbol, + currencySymbol = fromCurrency.network.currencySymbol, ), ) } @@ -290,10 +291,10 @@ internal class SwapNotificationsFactory( ) { if (hideFee) return val feeEnoughState = quoteModel.preparedSwapConfigState.feeState as? SwapFeeState.NotEnough ?: return - val needShowCoverWarning = quoteModel.preparedSwapConfigState.isBalanceEnough && + val shouldShowCoverWarning = quoteModel.preparedSwapConfigState.isBalanceEnough && quoteModel.permissionState !is PermissionDataState.PermissionLoading && feeEnoughState.feeCurrency != fromToken - if (needShowCoverWarning) { + if (shouldShowCoverWarning) { add( SwapNotificationUM.Error.UnableToCoverFeeWarning( fromToken = fromToken, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt index aab8a21eae..8d9c1bf701 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt @@ -11,7 +11,7 @@ internal data class SwapSelectTokenStateHolder( val unavailableTokens: ImmutableList, val tokensListData: TokenListUMData, val isBalanceHidden: Boolean, - val afterSearch: Boolean, + val isAfterSearch: Boolean, val onSearchEntered: (String) -> Unit, val onTokenSelected: (String) -> Unit, ) @@ -25,7 +25,7 @@ internal sealed class TokenToSelectState { val name: String, val symbol: String, val tokenIcon: CurrencyIconState, - val available: Boolean = true, + val isAvailable: Boolean = true, val addedTokenBalanceData: TokenBalanceData? = null, ) : TokenToSelectState() } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index 43985f8975..48a1a717c6 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -77,7 +77,7 @@ sealed class SwapCardState { data class SwapButton( @DrawableRes val walletInteractionIcon: Int?, - val enabled: Boolean, + val isEnabled: Boolean, val onClick: () -> Unit, ) @@ -94,7 +94,7 @@ sealed interface TransactionCardType { ) : TransactionCardType data class ReadOnly( - val showWarning: Boolean = false, + val shouldShowWarning: Boolean = false, val onWarningClick: (() -> Unit)? = null, override val inputError: InputError = InputError.Empty, override val accountTitleUM: AccountTitleUM? = null, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt index 906782071d..5156ce3e1e 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt @@ -9,7 +9,7 @@ data class SwapSuccessStateHolder( val txUrl: String, val fee: TextReference, val rate: TextReference, - val showStatusButton: Boolean, + val shouldShowStatusButton: Boolean, val providerName: TextReference, val providerType: TextReference, val providerIcon: String, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt index 9c3b0ea54d..77c343f43b 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt @@ -17,7 +17,7 @@ internal data object SwapSuccessStatePreview { fee = TextReference.Str("1 000 DAI ~ 1 000 MATIC"), providerName = TextReference.Str("1inch"), providerType = TextReference.Str(ExchangeProviderType.DEX.providerName), - showStatusButton = false, + shouldShowStatusButton = false, providerIcon = "", fromTitle = AccountTitleUM.Account( prefixText = stringReference("From"), diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt index 3946ea9358..067df047a5 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt @@ -58,14 +58,13 @@ private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) { } FooterBlock( readMore = content.readMore, - readMoreUrl = content.readMoreUrl, - onReadMoreClick = content.onReadMoreClick, + onReadMoreClick = { content.onReadMoreClick(content.readMoreUrl) }, ) } } @Composable -private fun FooterBlock(readMore: TextReference, readMoreUrl: String, onReadMoreClick: (String) -> Unit) { +private fun FooterBlock(readMore: TextReference, onReadMoreClick: () -> Unit) { val linkText = readMore.resolveReference() val fullString = stringResourceSafe(R.string.common_fee_selector_footer, linkText) val linkTextPosition = fullString.length - linkText.length @@ -81,7 +80,7 @@ private fun FooterBlock(readMore: TextReference, readMoreUrl: String, onReadMore val click = { i: Int -> val readMoreStyle = requireNotNull(annotatedString.spanStyles.getOrNull(1)) if (i in readMoreStyle.start..readMoreStyle.end) { - onReadMoreClick(readMoreUrl) + onReadMoreClick() } } @@ -102,7 +101,7 @@ private fun FooterBlock(readMore: TextReference, readMoreUrl: String, onReadMore private fun FeeItemsBlock(content: ChooseFeeBottomSheetConfig) { content.feeItems.forEachIndexed { index, feeItem -> val isSelected = feeItem.feeType == content.selectedFee - val showDivider = content.feeItems.lastIndex != index + val shouldShowDivider = content.feeItems.lastIndex != index val symbol = " ${feeItem.symbolCrypto}" val preDotText = "${feeItem.amountCrypto}$symbol" val postDot = feeItem.amountFiatFormatted @@ -117,7 +116,7 @@ private fun FeeItemsBlock(content: ChooseFeeBottomSheetConfig) { ellipsizeOffset = ellipsizeOffset, isSelected = isSelected, onSelect = { content.onSelectFeeType(feeItem.feeType) }, - showDivider = showDivider, + showDivider = shouldShowDivider, ) } FeeType.PRIORITY -> { @@ -129,7 +128,7 @@ private fun FeeItemsBlock(content: ChooseFeeBottomSheetConfig) { ellipsizeOffset = ellipsizeOffset, isSelected = isSelected, onSelect = { content.onSelectFeeType(feeItem.feeType) }, - showDivider = showDivider, + showDivider = shouldShowDivider, ) } } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt index ac6ebfd1b3..a610eaac34 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt @@ -132,16 +132,16 @@ private fun ProviderContentState( modifier = Modifier.padding(end = TangemTheme.dimens.spacing4), ) } - AnimatedContent(targetState = state.name, label = "") { + AnimatedContent(targetState = state.name, label = "") { name -> Text( - text = it, + text = name, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.primary1, ) } - AnimatedContent(targetState = state.type, label = "") { + AnimatedContent(targetState = state.type, label = "") { type -> Text( - text = it, + text = type, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), @@ -162,9 +162,9 @@ private fun ProviderContentState( end = TangemTheme.dimens.spacing56, ), ) { - AnimatedContent(targetState = state.subtitle, label = "") { + AnimatedContent(targetState = state.subtitle, label = "") { subtitle -> Text( - text = it.resolveReference(), + text = subtitle.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, overflow = TextOverflow.Ellipsis, @@ -179,9 +179,9 @@ private fun ProviderContentState( } else { TangemTheme.colors.text.warning } - AnimatedContent(targetState = state.percentLowerThenBest.value, label = "") { + AnimatedContent(targetState = state.percentLowerThenBest.value, label = "") { percentValue -> Text( - text = if (it > 0) "+$it%" else "$it%", + text = if (percentValue > 0) "+$percentValue%" else "$percentValue%", style = TangemTheme.typography.body2, color = textColor, modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), @@ -231,25 +231,25 @@ private fun ProviderUnavailableState( modifier = Modifier.padding(start = TangemTheme.dimens.spacing12), ) { Row { - AnimatedContent(targetState = state.name, label = "") { + AnimatedContent(targetState = state.name, label = "") { name -> Text( - text = it, + text = name, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } - AnimatedContent(targetState = state.type, label = "") { + AnimatedContent(targetState = state.type, label = "") { type -> Text( - text = it, + text = type, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.padding(start = TangemTheme.dimens.spacing4), ) } } - AnimatedContent(targetState = state.alertText, label = "") { + AnimatedContent(targetState = state.alertText, label = "") { alertText -> Text( - text = it.resolveReference(), + text = alertText.resolveReference(), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, modifier = Modifier.padding(top = TangemTheme.dimens.spacing6), diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 6369135b49..64ac9aa044 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -109,7 +109,7 @@ internal class StateBuilder( type = TransactionCardType.ReadOnly(), amountEquivalent = null, tokenIconUrl = initialCurrencyTo?.iconUrl, - tokenCurrency = initialCurrencyTo?.symbol ?: "", + tokenCurrency = initialCurrencyTo?.symbol.orEmpty(), token = null, amountTextFieldValue = null, canSelectAnotherToken = false, @@ -122,7 +122,7 @@ internal class StateBuilder( fee = FeeItemState.Empty, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = false, + isEnabled = false, onClick = {}, ), onRefresh = {}, @@ -181,7 +181,7 @@ internal class StateBuilder( fee = FeeItemState.Empty, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = false, + isEnabled = false, onClick = { }, ), changeCardsButtonState = ChangeCardsButtonState.DISABLED, @@ -246,7 +246,7 @@ internal class StateBuilder( fee = FeeItemState.Empty, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = false, + isEnabled = false, onClick = {}, ), providerState = ProviderState.Loading(), @@ -335,7 +335,7 @@ internal class StateBuilder( ), receiveCardData = SwapCardState.SwapCardData( type = TransactionCardType.ReadOnly( - showWarning = true, + shouldShowWarning = true, onWarningClick = actions.onReceiveCardWarningClick, accountTitleUM = getToCardAccountTitle(toAccount), ), @@ -368,7 +368,7 @@ internal class StateBuilder( fee = feeState, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = getSwapButtonEnabled(notifications), + isEnabled = getSwapButtonEnabled(notifications), onClick = actions.onSwapClick, ), changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible), @@ -397,17 +397,17 @@ internal class StateBuilder( private fun createTosState(swapProvider: SwapProvider): TosState { return TosState( - tosLink = swapProvider.termsOfUse?.let { + tosLink = swapProvider.termsOfUse?.let { termsUrl -> LegalState( title = resourceReference(R.string.common_terms_of_use), - link = it, + link = termsUrl, onClick = actions.onLinkClick, ) }, - policyLink = swapProvider.privacyPolicy?.let { + policyLink = swapProvider.privacyPolicy?.let { policyUrl -> LegalState( title = resourceReference(R.string.common_privacy_policy), - link = it, + link = policyUrl, onClick = actions.onLinkClick, ) }, @@ -420,12 +420,13 @@ internal class StateBuilder( } private fun getSwapButtonEnabled(notifications: ImmutableList): Boolean { - return notifications.none { - it is SwapNotificationUM.Error || it is NotificationUM.Error || - it is SwapNotificationUM.Warning.ExpressError || it is SwapNotificationUM.Warning.ExpressGeneralError || - it is SwapNotificationUM.Warning.NoAvailableTokensToSwap || - it is SwapNotificationUM.Warning.NeedReserveToCreateAccount || - it is SwapNotificationUM.Info.PermissionNeeded + return notifications.none { notification -> + notification is SwapNotificationUM.Error || notification is NotificationUM.Error || + notification is SwapNotificationUM.Warning.ExpressError || + notification is SwapNotificationUM.Warning.ExpressGeneralError || + notification is SwapNotificationUM.Warning.NoAvailableTokensToSwap || + notification is SwapNotificationUM.Warning.NeedReserveToCreateAccount || + notification is SwapNotificationUM.Info.PermissionNeeded } } @@ -495,7 +496,7 @@ internal class StateBuilder( fee = FeeItemState.Empty, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = false, + isEnabled = false, onClick = actions.onSwapClick, ), changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible), @@ -592,7 +593,7 @@ internal class StateBuilder( fee = FeeItemState.Empty, swapButton = SwapButton( walletInteractionIcon = walletInterationIcon(userWalletProvider()), - enabled = false, + isEnabled = false, onClick = { }, ), changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible), @@ -604,7 +605,7 @@ internal class StateBuilder( fun createSwapInProgressState(uiState: SwapStateHolder): SwapStateHolder { return uiState.copy( swapButton = uiState.swapButton.copy( - enabled = false, + isEnabled = false, ), ) } @@ -722,15 +723,17 @@ internal class StateBuilder( ) val selectTokenState = uiState.selectTokenState?.copy( isBalanceHidden = isBalanceHidden, - availableTokens = uiState.selectTokenState.availableTokens.map { - when (it) { + availableTokens = uiState.selectTokenState.availableTokens.map { tokenState -> + when (tokenState) { is TokenToSelectState.TokenToSelect -> { - it.copy( - addedTokenBalanceData = it.addedTokenBalanceData?.copy(isBalanceHidden = isBalanceHidden), + tokenState.copy( + addedTokenBalanceData = tokenState.addedTokenBalanceData?.copy( + isBalanceHidden = isBalanceHidden, + ), ) } is TokenToSelectState.Title -> { - it + tokenState } } }.toImmutableList(), @@ -804,7 +807,7 @@ internal class StateBuilder( fun loadingPermissionState(uiState: SwapStateHolder): SwapStateHolder { return uiState.copy( swapButton = uiState.swapButton.copy( - enabled = false, + isEnabled = false, ), permissionState = GiveTxPermissionState.InProgress, notifications = notificationsFactory.getApprovalInProgressStateNotification(uiState.notifications), @@ -837,7 +840,7 @@ internal class StateBuilder( txUrl = txUrl, providerName = stringReference(providerState.name), providerType = stringReference(providerState.type), - showStatusButton = shouldShowStatus, + shouldShowStatusButton = shouldShowStatus, providerIcon = providerState.iconUrl, rate = providerState.subtitle, fee = stringReference("${fee.feeCryptoFormattedWithNative} (${fee.feeFiatFormattedWithNative})"), @@ -877,7 +880,7 @@ internal class StateBuilder( txUrl = txUrl, providerName = stringReference(providerState.name), providerType = stringReference(providerState.type), - showStatusButton = false, + shouldShowStatusButton = false, providerIcon = providerState.iconUrl, rate = providerState.subtitle, fee = TextReference.EMPTY, @@ -1101,8 +1104,8 @@ internal class StateBuilder( onDismiss: () -> Unit, ): SwapStateHolder { val availableProvidersStates = providersStates.entries - .mapNotNull { - it.convertToProviderBottomSheetState( + .mapNotNull { entry -> + entry.convertToProviderBottomSheetState( pricesLowerBest = pricesLowerBest, onProviderSelect = actions.onProviderSelect, needApplyFCARestrictions = needApplyFCARestrictions, @@ -1139,19 +1142,19 @@ internal class StateBuilder( uiState.copy( bottomSheetConfig = uiState.bottomSheetConfig.copy( content = config.copy( - providers = providers.map { - val tokenInfo = tokenSwapInfoForProviders[it.id] - if (it is ProviderState.Content && tokenInfo != null) { + providers = providers.map { providerState -> + val tokenInfo = tokenSwapInfoForProviders[providerState.id] + if (providerState is ProviderState.Content && tokenInfo != null) { val rateString = tokenInfo.tokenAmount .getFormattedCryptoAmount(tokenInfo.cryptoCurrencyStatus.currency) - it.copy( + providerState.copy( subtitle = stringReference(rateString), - percentLowerThenBest = pricesLowerBest[it.id]?.let { percent -> + percentLowerThenBest = pricesLowerBest[providerState.id]?.let { percent -> PercentDifference.Value(percent) } ?: PercentDifference.Value(0f), ) } else { - it + providerState } }.toImmutableList(), ), @@ -1170,8 +1173,8 @@ internal class StateBuilder( ): SwapStateHolder { val config = ChooseFeeBottomSheetConfig( selectedFee = selectedFee, - onSelectFeeType = { - val selectedItem = when (it) { + onSelectFeeType = { feeType -> + val selectedItem = when (feeType) { FeeType.NORMAL -> txFeeState.normalFee FeeType.PRIORITY -> txFeeState.priorityFee } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index 486c515d86..3aa2c4d059 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -175,7 +175,7 @@ private fun TransactionCardData( balance = swapCardState.balance.orMaskWithStars(swapCardState.isBalanceHidden), textFieldValue = swapCardState.amountTextFieldValue, amountEquivalent = swapCardState.amountEquivalent, - tokenIconUrl = swapCardState.tokenIconUrl ?: "", + tokenIconUrl = swapCardState.tokenIconUrl.orEmpty(), tokenCurrency = swapCardState.tokenCurrency, priceImpact = priceImpact, networkIconRes = if (swapCardState.isNotNativeToken) swapCardState.networkIconRes else null, @@ -374,7 +374,7 @@ private fun MainButton(state: SwapStateHolder) { modifier = Modifier.fillMaxWidth(), text = stringResourceSafe(id = R.string.swapping_swap_action), iconResId = state.swapButton.walletInteractionIcon, - enabled = state.swapButton.enabled, + enabled = state.swapButton.isEnabled, onClick = state.swapButton.onClick, ) } @@ -437,7 +437,7 @@ private val state = SwapStateHolder( ), SwapNotificationUM.Warning.NoAvailableTokensToSwap("POLYGON"), ), - swapButton = SwapButton(enabled = true, onClick = {}, walletInteractionIcon = null), + swapButton = SwapButton(isEnabled = true, onClick = {}, walletInteractionIcon = null), onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {}, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index 39625f9e1f..6b0fa946f4 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -57,11 +57,11 @@ internal fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () val modifier = Modifier.padding(padding) when { state.availableTokens.isEmpty() && state.unavailableTokens.isEmpty() && - state.tokensListData.tokensList.isEmpty() && state.afterSearch -> { + state.tokensListData.tokensList.isEmpty() && state.isAfterSearch -> { TokensNotFound(modifier) } state.availableTokens.isEmpty() && state.unavailableTokens.isEmpty() && - state.tokensListData.tokensList.isEmpty() && !state.afterSearch -> { + state.tokensListData.tokensList.isEmpty() && !state.isAfterSearch -> { EmptyTokensList(modifier) } else -> { @@ -313,7 +313,7 @@ private fun TokenItem( .fillMaxWidth() .height(TangemTheme.dimens.size72) .clickable( - enabled = token.available, + enabled = token.isAvailable, onClick = onTokenClick, ) .padding( @@ -336,7 +336,7 @@ private fun TokenItem( EllipsisText( text = token.name, style = TangemTheme.typography.subtitle1, - color = if (token.available) { + color = if (token.isAvailable) { TangemTheme.colors.text.primary1 } else { TangemTheme.colors.text.tertiary @@ -366,7 +366,7 @@ private fun TokenItem( maxLines = 1, softWrap = false, overflow = TextOverflow.Visible, - color = if (token.available) { + color = if (token.isAvailable) { TangemTheme.colors.text.primary1 } else { TangemTheme.colors.text.tertiary @@ -417,7 +417,7 @@ private fun TokenScreenPreview() { availableTokens = listOf(title, token, token, token).toImmutableList(), unavailableTokens = listOf(title, token, token, token).toImmutableList(), tokensListData = TokenListUMData.EmptyList, - afterSearch = false, + isAfterSearch = false, isBalanceHidden = false, onSearchEntered = {}, onTokenSelected = {}, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt index 97e9c3dd4d..2d51569af0 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt @@ -48,7 +48,7 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) { SwapSuccessScreenButtons( textRes = R.string.common_close, txUrl = state.txUrl, - showStatusButton = state.showStatusButton, + shouldShowStatusButton = state.shouldShowStatusButton, onExploreClick = state.onExploreButtonClick, onStatusClick = state.onStatusButtonClick, onDoneClick = onBack, @@ -160,7 +160,7 @@ private fun SwapAmountBlock( private fun SwapSuccessScreenButtons( @StringRes textRes: Int, txUrl: String, - showStatusButton: Boolean, + shouldShowStatusButton: Boolean, onExploreClick: () -> Unit, onStatusClick: () -> Unit, onDoneClick: () -> Unit, @@ -178,7 +178,7 @@ private fun SwapSuccessScreenButtons( onClick = onExploreClick, modifier = Modifier.weight(1f), ) - if (showStatusButton) { + if (shouldShowStatusButton) { SpacerW12() SecondaryButtonIconStart( text = stringResourceSafe(id = R.string.express_cex_status_button_title), diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index a529440cd6..f344f04903 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -46,7 +46,6 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.SwapTokenScreenTestTags import com.tangem.core.ui.utils.ImageBackgroundContrastChecker -import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.CryptoPortfolioIcon import com.tangem.feature.swap.domain.models.ui.PriceImpact import com.tangem.feature.swap.models.TransactionCardType @@ -217,9 +216,9 @@ private fun Header(type: TransactionCardType, balance: String, modifier: Modifie } SpacerW16() if (balance.isNotBlank()) { - AnimatedContent(targetState = balance, label = "") { + AnimatedContent(targetState = balance, label = "") { balanceText -> Text( - text = it, + text = balanceText, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier @@ -318,15 +317,15 @@ private fun Content( style = TangemTheme.typography.body2, ) } else { - AnimatedContent(targetState = amountEquivalent, label = "") { + AnimatedContent(targetState = amountEquivalent, label = "") { amount -> Text( - text = it, + text = amount, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, ) } } - if (type.showWarning) { + if (type.shouldShowWarning) { SpacerW4() IconButton( onClick = { @@ -348,9 +347,9 @@ private fun Content( } } } else { - AnimatedContent(targetState = amountEquivalent, label = "") { + AnimatedContent(targetState = amountEquivalent, label = "") { amount -> Text( - text = it, + text = amount, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20), @@ -409,6 +408,7 @@ fun Token( } } +@Suppress("NullableToStringCall") @Composable private fun TokenIcon( tokenIconUrl: String, @@ -595,7 +595,7 @@ private fun TransactionCardPreview() { private fun TransactionCardPreviewWithPriceImpact() { TransactionCard( type = TransactionCardType.ReadOnly( - showWarning = true, + shouldShowWarning = true, accountTitleUM = AccountTitleUM.Account( prefixText = resourceReference(R.string.common_from), name = AccountNameUM.DefaultMain.value,