diff --git a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt index 82ab8a2925..3412cac097 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/DeepLinkFactory.kt @@ -87,7 +87,7 @@ internal class DeepLinkFactory @Inject constructor( fun checkRoutingReadiness(appRoute: AppRoute) { permittedAppRoute.value = when (appRoute) { AppRoute.Initial, - AppRoute.Home, + is AppRoute.Home, is AppRoute.Welcome, is AppRoute.PushNotification, is AppRoute.Disclaimer, diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt index 2b60066ea7..8212806067 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountStateConverter.kt @@ -13,6 +13,7 @@ import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format @@ -97,7 +98,9 @@ class AmountStateConverter( * @property maxEnterAmount max enter amount data * @property cryptoCurrencyStatus current cryptocurrency status * @property iconStateConverter currency icon converter + * @property isBalanceHidden is balance hidden status */ +@Suppress("LongParameterList") class AmountStateConverterV2( private val clickIntents: AmountScreenClickIntents, private val appCurrency: AppCurrency, @@ -105,6 +108,7 @@ class AmountStateConverterV2( private val maxEnterAmount: EnterAmountBoundary, private val iconStateConverter: CryptoCurrencyToIconStateConverter, private val isRedesignEnabled: Boolean, + private val isBalanceHidden: Boolean, ) : Converter { private val amountFieldConverter by lazy(LazyThreadSafetyMode.NONE) { @@ -120,6 +124,12 @@ class AmountStateConverterV2( val crypto = maxEnterAmount.amount.format { crypto(cryptoCurrencyStatus.currency) } val noFeeRate = cryptoCurrencyStatus.value.fiatRate.isNullOrZero() + if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Loading) { + return AmountState.Empty( + isRedesignEnabled = isRedesignEnabled, + ) + } + return AmountState.Data( title = value.title, availableBalance = if (isRedesignEnabled) { @@ -127,11 +137,12 @@ class AmountStateConverterV2( stringReference(crypto), stringReference(" $DOT "), stringReference(fiat), - ) + ).orMaskWithStars(isBalanceHidden) } else { resourceReference(R.string.common_crypto_fiat_format, wrappedList(crypto, fiat)) + .orMaskWithStars(isBalanceHidden) }, - availableBalanceShort = stringReference(crypto), + availableBalanceShort = stringReference(crypto).orMaskWithStars(isBalanceHidden), tokenName = stringReference(cryptoCurrencyStatus.currency.name), tokenIconState = iconStateConverter.convert(cryptoCurrencyStatus.currency), amountTextField = amountFieldConverter.convert(value.value), diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt index 7385fa9542..9639c7f781 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountBoundaryUpdateTransformer.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format @@ -28,6 +29,7 @@ class AmountBoundaryUpdateTransformer( private val maxEnterAmount: EnterAmountBoundary, private val appCurrency: AppCurrency, private val isRedesignEnabled: Boolean, + private val isBalanceHidden: Boolean, ) : Transformer { override fun transform(prevState: AmountState): AmountState { @@ -47,8 +49,8 @@ class AmountBoundaryUpdateTransformer( } return prevState.copy( - availableBalance = availableBalance, - availableBalanceShort = stringReference(crypto), + availableBalance = availableBalance.orMaskWithStars(isBalanceHidden), + availableBalanceShort = stringReference(crypto).orMaskWithStars(isBalanceHidden), ) } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt index 4a490037ae..4187ce21d2 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldChangeTransformer.kt @@ -39,7 +39,7 @@ class AmountFieldChangeTransformer( val amountTextField = prevState.amountTextField - if (value.isEmpty()) return prevState.emptyState() + if (value.isEmpty()) return prevState.emptyState(maxEnterAmount.fiatRate) val cryptoDecimals = amountTextField.cryptoAmount.decimals val fiatDecimals = amountTextField.fiatAmount.decimals @@ -75,7 +75,7 @@ class AmountFieldChangeTransformer( error = when { isExceedBalance -> resourceReference(R.string.send_validation_amount_exceeds_balance) isLessThanMinimumIfProvided -> { - val minimumAmount = minimumTransactionAmount?.amount.format { + val minimumAmount = minimumTransactionAmount.amount.format { crypto(cryptoCurrencyStatus.currency) } @@ -96,7 +96,7 @@ class AmountFieldChangeTransformer( ) } - private fun AmountState.Data.emptyState(): AmountState.Data { + private fun AmountState.Data.emptyState(fiatRate: BigDecimal?): AmountState.Data { return copy( isPrimaryButtonEnabled = false, reduceAmountBy = BigDecimal.ZERO, @@ -104,7 +104,7 @@ class AmountFieldChangeTransformer( value = "", fiatValue = "", cryptoAmount = amountTextField.cryptoAmount.copy(value = BigDecimal.ZERO), - fiatAmount = amountTextField.fiatAmount.copy(value = BigDecimal.ZERO), + fiatAmount = amountTextField.fiatAmount.copy(value = if (fiatRate != null) BigDecimal.ZERO else null), isError = false, keyboardOptions = KeyboardOptions( imeAction = ImeAction.None, diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt index 9c42eeac7e..f0baf3a1e9 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/field/AmountFieldConverter.kt @@ -100,8 +100,8 @@ class AmountFieldConverterV2( val cryptoAmount = cryptoDecimal.convertToAmount(cryptoCurrencyStatus.currency) val fiatRate = cryptoCurrencyStatus.value.fiatRate val (fiatValue, fiatDecimal) = when { - value.isEmpty() -> "" to BigDecimal.ZERO fiatRate.isNullOrZero() -> "" to null + value.isEmpty() -> "" to BigDecimal.ZERO else -> { val fiatDecimal = fiatRate?.multiply(cryptoDecimal) val fiatValue = fiatDecimal?.parseBigDecimal(FIAT_DECIMALS).orEmpty() diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt index a2db25c4dd..281270d273 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt @@ -93,6 +93,13 @@ object AmountStatePreviewData { amountTextField = amountWithValueState.amountTextField.copy(isFiatValue = false), ) + val amountStateV2WithoutRates = amountState.copy( + amountTextField = amountState.amountTextField.copy( + fiatAmount = amountState.amountTextField.fiatAmount.copy( + value = null, + ), + ), + ) val amountErrorState = amountWithValueState.copy( amountTextField = amountWithValueState.amountTextField.copy( isError = true, diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldV2.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldV2.kt index 96635bbc45..6360babb2d 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldV2.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldV2.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.BottomCenter import androidx.compose.ui.Alignment.Companion.TopCenter import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.graphicsLayer @@ -141,7 +142,6 @@ private fun AmountSecondary(amountUM: AmountState, onCurrencyChange: (Boolean) - AmountFieldCurrencyInfo( amountUM = amountUM, onCurrencyChange = onCurrencyChange, - ) AmountFieldError( isError = amountUM.amountTextField.isError, @@ -157,6 +157,8 @@ private fun AmountSecondary(amountUM: AmountState, onCurrencyChange: (Boolean) - @Composable private fun BoxScope.AmountFieldCurrencyInfo(amountUM: AmountState.Data, onCurrencyChange: (Boolean) -> Unit) { + val isFiatAvailable = amountUM.amountTextField.fiatAmount.value != null + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), @@ -168,6 +170,7 @@ private fun BoxScope.AmountFieldCurrencyInfo(amountUM: AmountState.Data, onCurre indication = null, onClick = { onCurrencyChange(!amountUM.amountTextField.isFiatValue) }, ) + .alpha(if (isFiatAvailable) 1f else 0f) .padding(4.dp), ) { val iconRotateState by animateFloatAsState( @@ -327,6 +330,7 @@ private class AmountFieldV2PreviewProvider : PreviewParameterProviderПодробности Проверьте подключение с интернетом или переключитесь на другую сеть Условия использования + Legacy адрес + Отправка средств в другой сети может повлечь потерю средств. Привет, команда поддержки, у меня возникла ошибка с кодом: %s Ошибка WalletConnect Вы использовали карту или кольцо от другого кошелька. Приложите карту или кольцо, связанную с этим кошельком. @@ -858,7 +860,7 @@ Это максимальное количество газа, которое будет потрачено на выполнение транзакции или контракта. Лимит газа предотвращает неожиданные или неограниченные расходы при выполнении транзакции. Цена газа Это стоимость, которую вы готовы заплатить за каждую единицу газа. Чем выше цена газа, тем быстрее ваша транзакция будет обработана. - Всё + Макс Максимальная сумма Комиссия не превысит Недопустимый Memo @@ -1081,6 +1083,7 @@ Лучшие курсы Интуитивный обмен в пару касаний — без сложностей и ожидания Проще простого + Обмен через провайдера В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя. В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %1$s от биржи обратно на адрес пользователя \n\nПроскальзывание провайдера составляет до %2$s В сумму включена комиссия провайдера сервиса. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index e12369b185..5babd8c525 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -393,6 +393,7 @@ Check your internet connection or switch to a different network Terms of service Default Address + Legacy Address Receive assets Sending assets in other networks will result in permanent loss. %s network @@ -985,6 +986,7 @@ Memo Check your network connection Network fee info unreachable + You send From From %s Gas limit diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/small/TangemIconButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/small/TangemIconButton.kt index c0d2dac09e..9cb8a2f121 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/small/TangemIconButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/small/TangemIconButton.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.res.TangemTheme @@ -30,6 +31,7 @@ import com.tangem.core.ui.res.TangemThemePreview * @param shape icon button shape * @param background background color * @param iconTint icon color + * @param innerPadding icon padding inside background area * * [Show in Figma](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=4105-1439&t=nnYBX1qCZmUNhBDf-4) */ @@ -41,6 +43,7 @@ fun TangemIconButton( shape: RoundedCornerShape = RoundedCornerShape(24.dp), background: Color = TangemTheme.colors.button.secondary, iconTint: Color = TangemTheme.colors.icon.secondary, + innerPadding: Dp = 4.dp, ) { Icon( painter = rememberVectorPainter(ImageVector.vectorResource(iconRes)), @@ -50,7 +53,7 @@ fun TangemIconButton( .size(24.dp) .clip(shape) .background(background) - .padding(4.dp) + .padding(innerPadding) .clickable( onClick = onClick, ), diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index 7179221efb..437fbe7c78 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -240,15 +240,19 @@ internal class DefaultTransactionRepository( val validator = walletManager as? TransactionValidator if (validator != null) { - val transactionData = walletManager.createTransaction( - amount = amount, - fee = fee ?: Fee.Common(amount = amount), - destination = destination, - ).copy( - extras = getMemoExtras(networkId = network.rawId, memo = memo), - ) + try { + val transactionData = walletManager.createTransaction( + amount = amount, + fee = fee ?: Fee.Common(amount = amount), + destination = destination, + ).copy( + extras = getMemoExtras(networkId = network.rawId, memo = memo), + ) - validator.validate(transactionData = transactionData) + validator.validate(transactionData = transactionData) + } catch (ex: Exception) { + Result.failure(ex) + } } else { Timber.e("${walletManager?.wallet?.blockchain} does not support transaction validation") Result.success(Unit) diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt index e22b05cb02..23990f9704 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt @@ -31,7 +31,7 @@ class DefaultWalletAddressServiceRepository( blockchain = blockchain, derivationPath = network.derivationPath.value, ) - walletManager?.wallet?.ens + walletManager?.wallet?.ens.takeIf { it.isNullOrEmpty().not() } } } @@ -50,7 +50,7 @@ class DefaultWalletAddressServiceRepository( ) if (walletManager is NameResolver) { - walletManager.reverseResolve(address.toByteArray()) + walletManager.reverseResolve(address) } else { ReverseResolveAddressResult.NotSupported } diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt index 728496ef05..d884942aeb 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt @@ -19,10 +19,9 @@ data class TokenReceiveConfig( data class ReceiveAddressModel( val nameService: NameService, val value: String, - val displayName: String, ) { enum class NameService { - Default, Ens + Default, Legacy, Ens } } diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt index 424ac7f642..cf752c7e38 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt @@ -4,6 +4,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN import com.tangem.core.analytics.models.AnalyticsParam.Key.ENS +import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM sealed class TokenReceiveNewAnalyticsEvent( @@ -27,11 +28,13 @@ sealed class TokenReceiveNewAnalyticsEvent( class ButtonCopyAddress( token: String, blockchainName: String, + tokenReceiveSource: TokenReceiveCopyActionSource, ) : TokenReceiveNewAnalyticsEvent( event = "Button - Copy Address", params = mapOf( TOKEN_PARAM to token, BLOCKCHAIN to blockchainName, + SOURCE to tokenReceiveSource.name, ), ) @@ -56,4 +59,8 @@ sealed class TokenReceiveNewAnalyticsEvent( BLOCKCHAIN to blockchainName, ), ) +} + +enum class TokenReceiveCopyActionSource { + Main, Token, Receive, QR } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetReverseResolvedEnsAddressUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetReverseResolvedEnsAddressUseCase.kt index 93f053818e..6d8eadf4e0 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetReverseResolvedEnsAddressUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/GetReverseResolvedEnsAddressUseCase.kt @@ -35,7 +35,13 @@ class GetReverseResolvedEnsAddressUseCase(private val walletAddressServiceReposi return when (reverseResolveAddressResult) { is ReverseResolveAddressResult.Error -> EnsAddress.Error(reverseResolveAddressResult.error) ReverseResolveAddressResult.NotSupported -> EnsAddress.NotSupported - is ReverseResolveAddressResult.Resolved -> EnsAddress.Address(reverseResolveAddressResult.name) + is ReverseResolveAddressResult.Resolved -> { + if (reverseResolveAddressResult.name.isEmpty()) { + EnsAddress.NotSupported + } else { + EnsAddress.Address(reverseResolveAddressResult.name) + } + } } } } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateTransactionUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateTransactionUseCase.kt index 499e222199..64db5bebf9 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateTransactionUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateTransactionUseCase.kt @@ -21,14 +21,12 @@ class ValidateTransactionUseCase( destination: String, userWalletId: UserWalletId, network: Network, - ): Either = Either.catch { - transactionRepository.validateTransaction( - amount = amount, - fee = fee, - memo = memo, - destination = destination, - userWalletId = userWalletId, - network = network, - ).fold(onSuccess = { Unit.right() }, onFailure = { it.left() }) - } + ): Either = transactionRepository.validateTransaction( + amount = amount, + fee = fee, + memo = memo, + destination = destination, + userWalletId = userWalletId, + network = network, + ).fold(onSuccess = { Unit.right() }, onFailure = { it.left() }) } \ No newline at end of file diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt index 70636eda60..10ae9abbf8 100644 --- a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt +++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt @@ -28,6 +28,7 @@ import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.core.wallets.error.SaveWalletError import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry @@ -75,6 +76,7 @@ internal class HomeModel @Inject constructor( private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase, private val urlOpener: UrlOpener, private val userWalletsListManager: UserWalletsListManager, + private val reduxStateHolder: ReduxStateHolder, @GlobalUiMessageSender private val uiMessageSender: UiMessageSender, ) : Model() { @@ -206,6 +208,7 @@ internal class HomeModel @Inject constructor( } }, ifRight = { + reduxStateHolder.onUserWalletSelected(userWallet) setLoading(false) sendSignedInCardAnalyticsEvent(scanResponse) appRouter.replaceAll(AppRoute.Wallet) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt index 5b6bd84ca2..b214ab5873 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt @@ -26,6 +26,7 @@ import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network +import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isMultiCurrency @@ -390,16 +391,17 @@ internal class MarketsPortfolioModel @Inject constructor( ReceiveAddressModel( nameService = ReceiveAddressModel.NameService.Ens, value = ens, - displayName = ens, ), ) } addresses.availableAddresses.map { address -> add( ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Default, + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, value = address.value, - displayName = "${cryptoCurrency.name} (${cryptoCurrency.symbol})", ), ) } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt index 632959d7e8..0439b74aea 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt @@ -209,16 +209,17 @@ internal class NFTReceiveModel @Inject constructor( ReceiveAddressModel( nameService = ReceiveAddressModel.NameService.Ens, value = ens, - displayName = ens, ), ) } addresses.availableAddresses.map { address -> add( ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Default, + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, value = address.value, - displayName = cryptoCurrency.symbol, ), ) } diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt index 55d91be4b5..b3dbf385fb 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/amount/analytics/CommonSendAmountAnalyticEvents.kt @@ -1,6 +1,8 @@ package com.tangem.features.send.v2.api.subcomponents.amount.analytics import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN +import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM import com.tangem.core.analytics.models.AnalyticsParam.Key.TYPE sealed class CommonSendAmountAnalyticEvents( @@ -22,7 +24,16 @@ sealed class CommonSendAmountAnalyticEvents( /** Max amount button clicked */ data class MaxAmountButtonClicked( val categoryName: String, - ) : CommonSendAmountAnalyticEvents(category = categoryName, event = "Max Amount Taped") + val token: String, + val blockchain: String, + ) : CommonSendAmountAnalyticEvents( + category = categoryName, + event = "Max Amount Taped", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), + ) enum class SelectedCurrencyType(val value: String) { Token("Token"), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt index 984330ba7d..f18192f9f9 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/entrypoint/model/SendEntryPointModel.kt @@ -69,6 +69,9 @@ internal class SendEntryPointModel @Inject constructor( } override fun onBack() { - router.pop() + modelScope.launch { + sendAmountUpdateTrigger.triggerUpdateAmount(lastSavedAmount) + router.pop() + } } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt index c486cce7a4..acaf93aac4 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt @@ -24,7 +24,6 @@ internal sealed class SendAnalyticEvents( val feeType: AnalyticsParam.FeeType, val blockchain: String, val nonceNotEmpty: Boolean, - private val ensStatus: AnalyticsParam.EnsStatus, ) : SendAnalyticEvents( event = "Transaction Sent Screen Opened", params = mapOf( @@ -32,10 +31,7 @@ internal sealed class SendAnalyticEvents( FEE_TYPE to feeType.value, BLOCKCHAIN to blockchain, NONCE to nonceNotEmpty.toString().capitalize(), - ENS_ADDRESS to when (ensStatus) { - AnalyticsParam.EnsStatus.EMPTY -> false.toString() - AnalyticsParam.EnsStatus.FULL -> true.toString() - }, + ENS_ADDRESS to (blockchain == "Ethereum").toString(), ), ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt index ad01b7a5c9..ab9490ce77 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt @@ -28,7 +28,6 @@ internal class SendAnalyticHelper @Inject constructor( feeType = feeType, blockchain = cryptoCurrency.network.name, nonceNotEmpty = feeSelectorUM.nonce != null, - ensStatus = getEnsStatus(sendUM), ), ) analyticsEventHandler.send( @@ -53,14 +52,4 @@ internal class SendAnalyticHelper @Inject constructor( else -> Basic.TransactionSent.MemoType.Null } } - - private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EnsStatus { - val blockchainAddressForEns = - (sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.blockchainAddress - return if (blockchainAddressForEns != null) { - AnalyticsParam.EnsStatus.FULL - } else { - AnalyticsParam.EnsStatus.EMPTY - } - } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index a59c2345b7..cd417bc2f5 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -72,6 +72,7 @@ internal class SendConfirmComponent( userWalletId = params.userWallet.walletId, cryptoCurrency = params.cryptoCurrencyStatus.currency, cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow, + isBalanceHidingFlow = params.isBalanceHidingFlow, ), onResult = model::onAmountResult, onClick = model::showEditAmount, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index 1fb318f5c1..5fcc8c9505 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -28,6 +28,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.requireColdWallet +import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase import com.tangem.domain.settings.NeverShowTapHelpUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase @@ -105,6 +106,7 @@ internal class SendConfirmModel @Inject constructor( private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger, private val feeReloadTrigger: SendFeeReloadTrigger, private val sendAmountReduceTrigger: SendAmountReduceTrigger, + private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, sendBalanceUpdaterFactory: SendBalanceUpdater.Factory, ) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback { @@ -121,6 +123,9 @@ internal class SendConfirmModel @Inject constructor( private val _uiState = MutableStateFlow(params.state) val uiState = _uiState.asStateFlow() + val isBalanceHiddenFlow: StateFlow + field = MutableStateFlow(false) + private val amountState get() = uiState.value.amountUM as? AmountState.Data private val destinationUM @@ -139,8 +144,16 @@ internal class SendConfirmModel @Inject constructor( reduceAmountBy = amountState?.reduceAmountBy.orZero(), isIgnoreReduce = amountState?.isIgnoreReduce == true, enteredDestination = destinationUM?.addressTextField?.actualAddress, - fee = feeSelectorUM?.selectedFee, - feeError = (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error, + fee = if (uiState.value.isRedesignEnabled) { + feeUMV2?.selectedFeeItem?.fee + } else { + feeSelectorUM?.selectedFee + }, + feeError = if (uiState.value.isRedesignEnabled) { + (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error + } else { + (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error + }, ) private var sendIdleTimer: Long = 0L @@ -156,6 +169,7 @@ internal class SendConfirmModel @Inject constructor( subscribeOnNotificationsUpdateTrigger() subscribeOnCheckFeeResultUpdates() initialState() + subscribeOnBalanceHidden() } fun updateState(state: SendUM) { @@ -511,6 +525,16 @@ internal class SendConfirmModel @Inject constructor( }.launchIn(modelScope) } + private fun subscribeOnBalanceHidden() { + getBalanceHidingSettingsUseCase() + .conflate() + .distinctUntilChanged() + .onEach { balanceHidingSettings -> + isBalanceHiddenFlow.update { balanceHidingSettings.isBalanceHidden } + } + .launchIn(modelScope) + } + private fun updateConfirmNotifications() { modelScope.launch { notificationsUpdateTrigger.triggerUpdate( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt index b23440cfeb..2a66af2d55 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt @@ -111,7 +111,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( val stackState by childStack.subscribeAsState() val state by model.uiState.collectAsStateWithLifecycle() - BackHandler(onBack = ::onChildBack) + BackHandler(onBack = model::onBackClick) SendContent( navigationUM = state.navigationUM, stackState = stackState, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt index 45ace3d940..0cd5ed9676 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/confirm/model/NFTSendConfirmModel.kt @@ -120,8 +120,16 @@ internal class NFTSendConfirmModel @Inject constructor( get() = ConfirmData( enteredDestination = destinationUM?.addressTextField?.actualAddress, enteredMemo = destinationUM?.memoTextField?.value, - fee = feeSelectorUM?.selectedFee, - feeError = (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error, + fee = if (uiState.value.isRedesignEnabled) { + (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Content)?.selectedFeeItem?.fee + } else { + feeSelectorUM?.selectedFee + }, + feeError = if (uiState.value.isRedesignEnabled) { + (uiState.value.feeSelectorUM as? FeeSelectorUMRedesigned.Error)?.error + } else { + (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error + }, ) private var sendIdleTimer: Long = 0L @@ -255,11 +263,15 @@ internal class NFTSendConfirmModel @Inject constructor( private fun initialState() { val confirmUM = uiState.value.confirmUM - val feeUM = uiState.value.feeUM + val isEmptyFee = if (uiState.value.isRedesignEnabled) { + uiState.value.feeSelectorUM !is FeeSelectorUMRedesigned.Content + } else { + uiState.value.feeUM is FeeUM.Empty + } modelScope.launch { val isShowTapHelp = isSendTapHelpEnabledUseCase().getOrElse { false } - if (confirmUM is ConfirmUM.Empty || feeUM is FeeUM.Empty) { + if (confirmUM is ConfirmUM.Empty || isEmptyFee) { _uiState.update { it.copy( confirmUM = NFTSendConfirmInitialStateTransformer( @@ -277,11 +289,17 @@ internal class NFTSendConfirmModel @Inject constructor( notificationsUpdateListener.hasErrorFlow .onEach { hasError -> _uiState.update { - val feeUM = it.feeUM as? FeeUM.Content - val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content + val isFeeNotNull = if (uiState.value.isRedesignEnabled) { + it.feeSelectorUM is FeeSelectorUMRedesigned.Content + } else { + val feeUM = it.feeUM as? FeeUM.Content + val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content + feeSelectorUM != null + } + it.copy( confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy( - isPrimaryButtonEnabled = !hasError && feeSelectorUM != null, + isPrimaryButtonEnabled = !hasError && isFeeNotNull, ) ?: it.confirmUM, ) } @@ -290,9 +308,8 @@ internal class NFTSendConfirmModel @Inject constructor( } private fun verifyAndSendTransaction() { - val destination = destinationUM?.addressTextField?.actualAddress ?: return - val memo = destinationUM?.memoTextField?.value - val fee = feeSelectorUM?.selectedFee ?: return + val destination = confirmData.enteredDestination ?: return + val fee = confirmData.fee ?: return val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value ?: return val sdkNFTAsset = NFTSdkAssetConverter.convertBack(params.nftAsset) @@ -302,7 +319,7 @@ internal class NFTSendConfirmModel @Inject constructor( ownerAddress = ownerAddress, nftAsset = sdkNFTAsset.second, fee = fee, - memo = memo, + memo = confirmData.enteredMemo, destinationAddress = destination, userWalletId = userWallet.walletId, network = cryptoCurrency.network, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 90a8753b4a..11d9d7ba78 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -28,6 +28,7 @@ import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.features.nft.entity.NFTSendSuccessTrigger import com.tangem.features.send.v2.api.NFTSendComponent import com.tangem.features.send.v2.api.SendFeatureToggles import com.tangem.features.send.v2.api.entity.FeeSelectorUM @@ -74,6 +75,7 @@ internal class NFTSendModel @Inject constructor( private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, private val alertFactory: SendConfirmAlertFactory, private val sendFeatureToggles: SendFeatureToggles, + private val nftSendSuccessTrigger: NFTSendSuccessTrigger, ) : Model(), SendNFTComponentCallback, NFTSendSuccessComponent.ModelCallback { val params: NFTSendComponent.Params = paramsContainer.require() @@ -119,6 +121,11 @@ internal class NFTSendModel @Inject constructor( } override fun onBackClick() { + if (currentRouteFlow.value == ConfirmSuccess) { + modelScope.launch { + nftSendSuccessTrigger.triggerSuccessNFTSend() + } + } router.pop() } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/NFTSendSuccessComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/NFTSendSuccessComponent.kt index 76f7329658..e651f4f298 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/NFTSendSuccessComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/NFTSendSuccessComponent.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.common.ui.navigationButtons.NavigationModelCallback import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel @@ -85,7 +86,7 @@ internal class NFTSendSuccessComponent @AssistedInject constructor( val callback: ModelCallback, ) - interface ModelCallback { + interface ModelCallback : NavigationModelCallback { fun onResult(nftSendUM: NFTSendUM) } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/model/NFTSendSuccessModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/model/NFTSendSuccessModel.kt index 9352ec6898..7fc3951005 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/model/NFTSendSuccessModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/success/model/NFTSendSuccessModel.kt @@ -1,7 +1,6 @@ package com.tangem.features.send.v2.sendnft.success.model import androidx.compose.runtime.Stable -import com.tangem.common.routing.AppRouter import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.core.analytics.api.AnalyticsEventHandler @@ -31,7 +30,6 @@ internal class NFTSendSuccessModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, - private val appRouter: AppRouter, private val urlOpener: UrlOpener, private val shareManager: ShareManager, ) : Model() { @@ -64,16 +62,14 @@ internal class NFTSendSuccessModel @Inject constructor( isValid = true, ), ) - appRouter.pop() + params.callback.onBackClick() }, primaryButton = NavigationButton( textReference = resourceReference(R.string.common_close), iconRes = null, isEnabled = true, isHapticClick = false, - onClick = { - appRouter.pop() - }, + onClick = params.callback::onBackClick, ), prevButton = null, secondaryPairButtonsUM = NavigationButton( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt index 925d3780df..14a25b17b7 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt @@ -21,6 +21,7 @@ internal sealed class SendAmountComponentParams { abstract val isRedesignEnabled: Boolean abstract val cryptoCurrency: CryptoCurrency abstract val cryptoCurrencyStatusFlow: StateFlow + abstract val isBalanceHidingFlow: StateFlow data class AmountParams( override val state: AmountState, @@ -31,9 +32,9 @@ internal sealed class SendAmountComponentParams { override val isRedesignEnabled: Boolean, override val cryptoCurrency: CryptoCurrency, override val cryptoCurrencyStatusFlow: StateFlow, + override val isBalanceHidingFlow: StateFlow, val callback: ModelCallback, val currentRoute: StateFlow, - val isBalanceHidingFlow: StateFlow, ) : SendAmountComponentParams() data class AmountBlockParams( @@ -45,6 +46,7 @@ internal sealed class SendAmountComponentParams { override val isRedesignEnabled: Boolean, override val cryptoCurrency: CryptoCurrency, override val cryptoCurrencyStatusFlow: StateFlow, + override val isBalanceHidingFlow: StateFlow, val userWallet: UserWallet, val blockClickEnableFlow: StateFlow, ) : SendAmountComponentParams() diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index 72f75f5424..b69ef65add 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -28,6 +28,7 @@ import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.send.v2.api.SendFeatureToggles import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents @@ -66,6 +67,7 @@ internal class SendAmountModel @Inject constructor( private val getUserWalletUseCase: GetUserWalletUseCase, private val rampStateManager: RampStateManager, private val sendAmountAlertFactory: SendAmountAlertFactory, + private val getWalletsUseCase: GetWalletsUseCase, ) : Model(), SendAmountClickIntents { private val params: SendAmountComponentParams = paramsContainer.require() @@ -102,6 +104,7 @@ internal class SendAmountModel @Inject constructor( subscribeOnAmountReduceToTriggerUpdates() subscribeOnAmountIgnoreReduceTriggerUpdates() subscribeOnAmountUpdateTriggerUpdates() + subscribeOnBalanceHiddenUpdates() } private fun initAppCurrency() { @@ -119,6 +122,20 @@ internal class SendAmountModel @Inject constructor( }.launchIn(modelScope) } + private fun subscribeOnBalanceHiddenUpdates() { + params.isBalanceHidingFlow.onEach { isBalanceHidden -> + _uiState.update( + AmountBoundaryUpdateTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = maxAmountBoundary, + appCurrency = appCurrency, + isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled, + isBalanceHidden = params.isBalanceHidingFlow.value, + ), + ) + }.launchIn(modelScope) + } + private fun subscribeOnCryptoCurrencyStatusFlow() { params.cryptoCurrencyStatusFlow .onEach { newCryptoCurrencyStatus -> @@ -150,6 +167,7 @@ internal class SendAmountModel @Inject constructor( maxEnterAmount = maxAmountBoundary, appCurrency = appCurrency, isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled, + isBalanceHidden = params.isBalanceHidingFlow.value, ), ) } else { @@ -160,6 +178,7 @@ internal class SendAmountModel @Inject constructor( private fun initialState() { if (uiState.value is AmountState.Empty && userWallet != null) { + val isSingleWallet = getWalletsUseCase.invokeSync().size == 1 _uiState.update { AmountStateConverterV2( clickIntents = this, @@ -168,12 +187,17 @@ internal class SendAmountModel @Inject constructor( maxEnterAmount = maxAmountBoundary, iconStateConverter = CryptoCurrencyToIconStateConverter(), isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled, + isBalanceHidden = params.isBalanceHidingFlow.value, ).convert( AmountParameters( - title = resourceReference( - R.string.send_from_wallet_name, - WrappedList(listOf(userWallet?.name.orEmpty())), // TODO [REDACTED_TASK_KEY] - ), + title = if (isSingleWallet) { + resourceReference(R.string.send_from_title) + } else { + resourceReference( + R.string.send_from_wallet_name, + WrappedList(listOf(userWallet?.name.orEmpty())), // TODO [REDACTED_TASK_KEY] + ) + }, value = "", ), ) @@ -218,7 +242,11 @@ internal class SendAmountModel @Inject constructor( ), ) analyticsEventHandler.send( - CommonSendAmountAnalyticEvents.MaxAmountButtonClicked(categoryName = analyticsCategoryName), + CommonSendAmountAnalyticEvents.MaxAmountButtonClicked( + categoryName = analyticsCategoryName, + token = params.cryptoCurrency.symbol, + blockchain = params.cryptoCurrency.network.name, + ), ) } @@ -257,6 +285,11 @@ internal class SendAmountModel @Inject constructor( private fun confirmConvertToToken() { val amountParams = params as? SendAmountComponentParams.AmountParams ?: return val amountFieldData = uiState.value as? AmountState.Data + _uiState.update { + (it as? AmountState.Data)?.copy( + isPrimaryButtonEnabled = false, + ) ?: it + } amountParams.callback.onConvertToAnotherToken(amountFieldData?.amountTextField?.value.orEmpty()) } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index d68153cd9e..8d73d20253 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -106,8 +106,8 @@ internal class SendDestinationModel @Inject constructor( ), ) } - initSenderAddress() } + initSenderAddress() } fun updateState(destinationUM: DestinationUM) { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt index da00a9307b..37519c11bd 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/notifications/model/NotificationsModel.kt @@ -287,7 +287,7 @@ internal class NotificationsModel @Inject constructor( ) { val validationError = validateTransactionUseCase( userWalletId = userWalletId, - amount = sendingAmount.convertToSdkAmount(currency), + amount = enteredAmount.convertToSdkAmount(currency), fee = fee, memo = memo, destination = destinationAddress, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 3c4c055991..b2e38a4172 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -35,6 +35,7 @@ import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase import com.tangem.domain.swap.usecase.SelectInitialPairUseCase import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.transaction.usecase.GetAllowanceUseCase +import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener @@ -91,6 +92,7 @@ internal class SwapAmountModel @Inject constructor( private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger, private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase, private val analyticsEventHandler: AnalyticsEventHandler, + private val getWalletsUseCase: GetWalletsUseCase, ) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback { private val params: SwapAmountComponentParams = paramsContainer.require() @@ -105,10 +107,10 @@ internal class SwapAmountModel @Inject constructor( private var secondaryMaximumAmountBoundary: EnterAmountBoundary? = null private var secondaryMinimumAmountBoundary: EnterAmountBoundary? = null - var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country) + private var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country) val bottomSheetNavigation: SlotNavigation = SlotNavigation() - var showBestRateAnimation: Boolean = false + private var showBestRateAnimation: Boolean = false val uiState: StateFlow field = MutableStateFlow(params.amountUM) @@ -131,10 +133,14 @@ internal class SwapAmountModel @Inject constructor( subscribeOnAmountReduceByTriggerUpdates() subscribeOnAmountIgnoreReduceTriggerUpdates() subscribeOnReloadQuotesTriggerUpdates() + subscribeOnBalanceHiddenUpdates() } fun onStart() { - startLoadingQuotesTask(isSilentReload = false) + quoteTaskScheduler.scheduleTask( + scope = modelScope, + task = loadQuotesTask(), + ) } fun onStop() { @@ -213,7 +219,11 @@ internal class SwapAmountModel @Inject constructor( override fun onMaxValueClick() { analyticsEventHandler.send( - CommonSendAmountAnalyticEvents.MaxAmountButtonClicked(categoryName = params.analyticsCategoryName), + CommonSendAmountAnalyticEvents.MaxAmountButtonClicked( + categoryName = params.analyticsCategoryName, + token = primaryCryptoCurrency.symbol, + blockchain = primaryCryptoCurrency.network.name, + ), ) uiState.transformerUpdate( SwapAmountValueMaxTransformer( @@ -319,12 +329,29 @@ internal class SwapAmountModel @Inject constructor( ) } + private fun subscribeOnBalanceHiddenUpdates() { + params.isBalanceHidingFlow.onEach { isHidden -> + val isSingleWallet = getWalletsUseCase.invokeSync().size == 1 + uiState.transformerUpdate( + SwapAmountBalanceHiddenTransformer( + isBalanceHidden = isHidden, + isSingleWallet = isSingleWallet, + userWallet = userWallet, + appCurrency = appCurrency, + swapDirection = swapDirection, + clickIntents = this, + ), + ) + }.launchIn(modelScope) + } + private fun confirmSendWithSwapClose() { val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus if (primaryCryptoCurrencyStatus != null) { + val isSingleWallet = getWalletsUseCase.invokeSync().size == 1 uiState.transformerUpdate( SwapAmountPrimaryReadyStateTransformer( userWallet = userWallet, @@ -334,6 +361,7 @@ internal class SwapAmountModel @Inject constructor( clickIntents = this, isBalanceHidden = params.isBalanceHidingFlow.value, showBestRateAnimation = showBestRateAnimation, + isSingleWallet = isSingleWallet, ), ) } @@ -359,6 +387,7 @@ internal class SwapAmountModel @Inject constructor( ), ) } else { + val isSingleWallet = getWalletsUseCase.invokeSync().size == 1 uiState.transformerUpdate( SwapAmountPrimaryReadyStateTransformer( userWallet = userWallet, @@ -368,6 +397,7 @@ internal class SwapAmountModel @Inject constructor( clickIntents = this, isBalanceHidden = params.isBalanceHidingFlow.value, showBestRateAnimation = showBestRateAnimation, + isSingleWallet = isSingleWallet, ), ) } @@ -465,6 +495,7 @@ internal class SwapAmountModel @Inject constructor( val primaryStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus if (secondaryStatus != null && primaryStatus != null) { initCurrencies(primaryStatus, secondaryStatus) + val isSingleWallet = getWalletsUseCase.invokeSync().size == 1 uiState.transformerUpdate( SwapAmountSecondaryReadyStateTransformer( userWallet = userWallet, @@ -476,6 +507,7 @@ internal class SwapAmountModel @Inject constructor( clickIntents = this@SwapAmountModel, isBalanceHidden = params.isBalanceHidingFlow.value, showBestRateAnimation = showBestRateAnimation, + isSingleWallet = isSingleWallet, ), ) startLoadingQuotesTask(isSilentReload = false) @@ -533,18 +565,17 @@ internal class SwapAmountModel @Inject constructor( } as? AmountState.Data val fromAmountValue = fromAmount?.amountTextField?.cryptoAmount?.value.orZero() - - if (fromAmount?.amountTextField?.isError == true || fromAmountValue.isNullOrZero()) { + val isAmountScreen = params is SwapAmountComponentParams.AmountParams + val isAmountError = fromAmount?.amountTextField?.isError == true || fromAmountValue.isNullOrZero() + if (isAmountScreen && isAmountError) { uiState.transformerUpdate(SwapQuoteEmptyStateTransformer) return } - val swapGroups = state.swapCurrencies.getGroupWithDirection(state.swapDirection) - - uiState.transformerUpdate(SwapQuoteLoadingStateTransformer) + if (!isSilentReload) { uiState.transformerUpdate(SwapQuoteLoadingStateTransformer) } modelScope.launch { - val quotes = swapGroups.available.filter { + val quotes = state.swapCurrencies.getGroupWithDirection(state.swapDirection).available.filter { it.currencyStatus.currency.id == toCryptoCurrency.id }.flatMap { it.providers @@ -591,7 +622,6 @@ internal class SwapAmountModel @Inject constructor( needApplyFcaRestrictions = userCountry.needApplyFCARestrictions(), ), ) - feeSelectorReloadTrigger.triggerUpdate() } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt index fd83068f8f..1962a4d356 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountFieldConverter.kt @@ -25,6 +25,7 @@ internal class SwapAmountFieldConverter( private val userWallet: UserWallet, private val appCurrency: AppCurrency, private val clickIntents: AmountScreenClickIntents, + private val isSingleWallet: Boolean, ) { private val iconStateConverter = CryptoCurrencyToIconStateConverter() @@ -48,13 +49,18 @@ internal class SwapAmountFieldConverter( maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus), iconStateConverter = iconStateConverter, isRedesignEnabled = true, + isBalanceHidden = isBalanceHidden, ).convert( AmountParameters( - title = combinedReference( - resourceReference(R.string.send_from_wallet_android), - stringReference(" "), - stringReference(userWallet.name), - ), + title = if (isSingleWallet) { + resourceReference(R.string.send_from_title) + } else { + combinedReference( + resourceReference(R.string.send_from_wallet_android), + stringReference(" "), + stringReference(userWallet.name), + ) + }, value = "", ), ), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt new file mode 100644 index 0000000000..52fc0217cd --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountBalanceHiddenTransformer.kt @@ -0,0 +1,66 @@ +package com.tangem.features.swap.v2.impl.amount.model.transformers + +import com.tangem.common.ui.amountScreen.AmountScreenClickIntents +import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.swap.models.SwapDirection +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM +import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountFieldConverter +import com.tangem.utils.transformer.Transformer + +internal class SwapAmountBalanceHiddenTransformer( + private val isBalanceHidden: Boolean, + private val isSingleWallet: Boolean, + private val userWallet: UserWallet, + private val appCurrency: AppCurrency, + private val swapDirection: SwapDirection, + private val clickIntents: AmountScreenClickIntents, +) : Transformer { + + override fun transform(prevState: SwapAmountUM): SwapAmountUM { + val content = prevState as? SwapAmountUM.Content ?: return prevState + + val amountFieldConverter = SwapAmountFieldConverter( + swapDirection = swapDirection, + isBalanceHidden = isBalanceHidden, + userWallet = userWallet, + appCurrency = appCurrency, + clickIntents = clickIntents, + isSingleWallet = isSingleWallet, + ) + + val recalculatedPrimary = amountFieldConverter.convert( + selectedType = SwapAmountType.From, + cryptoCurrencyStatus = content.primaryCryptoCurrencyStatus, + ) as SwapAmountFieldUM.Content + + val oldPrimary = content.primaryAmount as? SwapAmountFieldUM.Content + + val mergedAmountField = if ( + oldPrimary?.amountField is AmountState.Data && recalculatedPrimary.amountField is AmountState.Data + ) { + val oldData = oldPrimary.amountField + val newData = recalculatedPrimary.amountField + newData.copy( + amountTextField = oldData.amountTextField, + selectedButton = oldData.selectedButton, + isPrimaryButtonEnabled = oldData.isPrimaryButtonEnabled, + isSegmentedButtonsEnabled = oldData.isSegmentedButtonsEnabled, + isEditingDisabled = oldData.isEditingDisabled, + reduceAmountBy = oldData.reduceAmountBy, + isIgnoreReduce = oldData.isIgnoreReduce, + ) + } else { + recalculatedPrimary.amountField + } + + val updatedPrimaryAmount = recalculatedPrimary.copy( + amountField = mergedAmountField, + ) + + return content.copy(primaryAmount = updatedPrimaryAmount) + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt index 3279e896ab..b6ff17ca44 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -24,6 +24,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, private val showBestRateAnimation: Boolean, + private val isSingleWallet: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -32,6 +33,7 @@ internal class SwapAmountPrimaryReadyStateTransformer( userWallet = userWallet, appCurrency = appCurrency, clickIntents = clickIntents, + isSingleWallet = isSingleWallet, ) override fun transform(prevState: SwapAmountUM): SwapAmountUM { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index 7a0a94e535..da85780483 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -25,6 +25,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, private val showBestRateAnimation: Boolean, + private val isSingleWallet: Boolean, ) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( @@ -33,6 +34,7 @@ internal class SwapAmountSecondaryReadyStateTransformer( userWallet = userWallet, appCurrency = appCurrency, clickIntents = clickIntents, + isSingleWallet = isSingleWallet, ) override fun transform(prevState: SwapAmountUM): SwapAmountUM { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index adfcc37a91..941fa7e8ec 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -34,8 +34,10 @@ internal class SwapAmountSetQuotesTransformer( val sortedQuotes = quotes.sortedWith(SwapQuotesComparator) val bestQuote = findBestQuote(quotes) ?: SwapQuoteUM.Empty + val quotesWithDiff = getQuotesWithDiff(sortedQuotes, bestQuote, isSingleProvider) val selectedQuote = if (isSilentReload && prevState.selectedQuote !is SwapQuoteUM.Loading) { - prevState.selectedQuote + quotesWithDiff.firstOrNull { it.provider?.providerId == prevState.selectedQuote.provider?.providerId } + ?: prevState.selectedQuote } else { (bestQuote as? SwapQuoteUM.Content)?.copy( diffPercent = DifferencePercent.Best, @@ -55,7 +57,7 @@ internal class SwapAmountSetQuotesTransformer( if (updatedState !is SwapAmountUM.Content) return prevState return updatedState.copy( - isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled && quotes.isNotEmpty(), + isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled && quotesWithDiff.isNotEmpty(), swapQuotes = getQuotesWithDiff(sortedQuotes, bestQuote, isSingleProvider), ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt index c364fd36a0..639f994b78 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt @@ -45,6 +45,7 @@ internal class SwapAmountValueChangeTransformer( ) return (updatedState as? SwapAmountUM.Content)?.copy( + isPrimaryButtonEnabled = false, selectedQuote = if (updatedState.isPrimaryButtonEnabled) { SwapQuoteUM.Empty } else { diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt index 59c38f1c94..abc50a6e78 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt @@ -16,31 +16,34 @@ internal class SwapAmountValueMaxTransformer( override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState - return prevState - .copy(selectedQuote = SwapQuoteUM.Loading) - .updateAmount( - onPrimaryAmount = { primaryStatus -> + val updatedState = prevState.updateAmount( + onPrimaryAmount = { primaryStatus -> + copy( + amountField = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = primaryStatus, + maxAmount = primaryMaximumAmountBoundary, + minAmount = primaryMinimumAmountBoundary, + ).transform(prevState.primaryAmount.amountField), + ) + }, + onSecondaryAmount = { secondaryStatus -> + if (secondaryMaximumAmountBoundary != null) { copy( amountField = AmountFieldSetMaxAmountTransformer( - cryptoCurrencyStatus = primaryStatus, - maxAmount = primaryMaximumAmountBoundary, - minAmount = primaryMinimumAmountBoundary, - ).transform(prevState.primaryAmount.amountField), + cryptoCurrencyStatus = secondaryStatus, + maxAmount = secondaryMaximumAmountBoundary, + minAmount = secondaryMinimumAmountBoundary, + ).transform(prevState.secondaryAmount.amountField), ) - }, - onSecondaryAmount = { secondaryStatus -> - if (secondaryMaximumAmountBoundary != null) { - copy( - amountField = AmountFieldSetMaxAmountTransformer( - cryptoCurrencyStatus = secondaryStatus, - maxAmount = secondaryMaximumAmountBoundary, - minAmount = secondaryMinimumAmountBoundary, - ).transform(prevState.secondaryAmount.amountField), - ) - } else { - this - } - }, - ) + } else { + this + } + }, + ) + + return (updatedState as? SwapAmountUM.Content)?.copy( + isPrimaryButtonEnabled = false, + selectedQuote = SwapQuoteUM.Loading, + ) ?: updatedState } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt index 4a7a127735..317f0d733f 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderStateConverter.kt @@ -42,7 +42,7 @@ internal class SwapProviderStateConverter( val additionalBadge = when { needApplyFCARestrictions && provider.isRestrictedByFCA() -> AdditionalBadge.FCAWarningList - isNeedBestRateBadge && isBestRate && !needApplyFCARestrictions -> AdditionalBadge.BestTrade + isNeedBestRateBadge && isBestRate -> AdditionalBadge.BestTrade else -> AdditionalBadge.Empty } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt index bc241e0ec0..712782f490 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt @@ -94,13 +94,15 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( ) activeComponent.updateState(model.uiState.value.destinationUM) } - is SendWithSwapConfirmComponent -> if (model.currentRoute.value.isEditMode) { + is SendWithSwapConfirmComponent -> { analyticsEventHandler.send( CommonSendAnalyticEvents.ConfirmationScreenOpened( categoryName = model.analyticCategoryName, ), ) - activeComponent.updateState(model.uiState.value) + if (model.currentRoute.value.isEditMode) { + activeComponent.updateState(model.uiState.value) + } } } model.currentRoute.emit(stack.active.configuration) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt index ad1927f7cd..6daa91c3d8 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt @@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_BLOCKCHAIN import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN @@ -24,7 +25,7 @@ internal sealed class SendWithSwapAnalyticEvents( event = "Send With Swap In Progress Screen Opened", params = mapOf( PROVIDER to providerName, - "Commission" to if (feeType is AnalyticsParam.FeeType.Normal) "Market" else "Fast", + FEE_TYPE to if (feeType is AnalyticsParam.FeeType.Normal) "Market" else "Fast", SEND_TOKEN to fromToken.symbol, RECEIVE_TOKEN to toToken.symbol, SEND_BLOCKCHAIN to fromToken.network.name, diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt index 83b0cf3b6f..a6914ac4c4 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt @@ -77,7 +77,7 @@ internal class DefaultTokenReceiveComponent @AssistedInject constructor( is TokenReceiveRoutes.QrCode -> TokenReceiveQrCodeComponent( appComponentContext = appComponentContext, params = TokenReceiveQrCodeComponent.TokenReceiveQrCodeParams( - network = model.params.config.cryptoCurrency.network.name, + cryptoCurrency = model.params.config.cryptoCurrency, address = model.state.value.addresses[config.addressId] ?: error("Address has to be there"), callback = model, onDismiss = ::dismiss, diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt index 7ece6e356d..692e3ae990 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt @@ -8,6 +8,7 @@ import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.features.tokenreceive.entity.ReceiveAddress import com.tangem.features.tokenreceive.model.TokenReceiveAssetsModel import com.tangem.features.tokenreceive.ui.TokenReceiveAssetsContent @@ -29,7 +30,7 @@ internal class TokenReceiveAssetsComponent( internal interface TokenReceiveAssetsModelCallback { fun onQrCodeClick(id: Int) - fun onCopyClick(id: Int) + fun onCopyClick(id: Int, source: TokenReceiveCopyActionSource) } data class TokenReceiveAssetsParams( diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt index 8191f9a208..c33bcca97f 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt @@ -7,6 +7,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.features.tokenreceive.entity.ReceiveAddress import com.tangem.features.tokenreceive.model.TokenReceiveQrCodeModel import com.tangem.features.tokenreceive.ui.TokenReceiveQrCodeContent @@ -25,13 +27,13 @@ internal class TokenReceiveQrCodeComponent( } internal interface TokenReceiveQrCodeModelCallback { - fun onCopyClick(id: Int) + fun onCopyClick(id: Int, source: TokenReceiveCopyActionSource) fun onShareClick(address: String) } data class TokenReceiveQrCodeParams( val id: Int, - val network: String, + val cryptoCurrency: CryptoCurrency, val address: ReceiveAddress, val callback: TokenReceiveQrCodeModelCallback, val onDismiss: () -> Unit, diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt index 960dda1224..b5264d8402 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/ReceiveAddress.kt @@ -1,16 +1,24 @@ package com.tangem.features.tokenreceive.entity +import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference internal data class ReceiveAddress( val value: String, val type: Type, ) { + + @Immutable sealed interface Type { data object Ens : Type - data class Default( - val displayName: TextReference, - ) : Type + sealed interface Primary : Type { + + val displayName: TextReference + + data class Default(override val displayName: TextReference) : Primary + + data class Legacy(override val displayName: TextReference) : Primary + } } } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt index bf37b011e3..5790e3e692 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt @@ -6,6 +6,7 @@ import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.features.tokenreceive.component.TokenReceiveAssetsComponent import com.tangem.features.tokenreceive.entity.ReceiveAddress @@ -38,13 +39,17 @@ internal class TokenReceiveAssetsModel @Inject constructor( internal val state: StateFlow field = MutableStateFlow( ReceiveAssetsUM( - onCopyClick = params.callback::onCopyClick, + onCopyClick = { + params.callback.onCopyClick( + id = it, + source = TokenReceiveCopyActionSource.Receive, + ) + }, onOpenQrCodeClick = params.callback::onQrCodeClick, addresses = params.addresses, showMemoDisclaimer = params.showMemoDisclaimer, isEnsResultLoading = false, notificationConfigs = params.notificationConfigs, - fullName = params.fullName, ), ) diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt index 183d11d046..784431f702 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt @@ -12,19 +12,22 @@ import com.tangem.core.ui.R import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.models.Asset import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.ens.EnsAddress import com.tangem.domain.models.network.Network import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.domain.transaction.usecase.GetReverseResolvedEnsAddressUseCase import com.tangem.features.tokenreceive.TokenReceiveComponent import com.tangem.features.tokenreceive.component.TokenReceiveModelCallback import com.tangem.features.tokenreceive.entity.ReceiveAddress +import com.tangem.features.tokenreceive.entity.ReceiveAddress.Type.Ens +import com.tangem.features.tokenreceive.entity.ReceiveAddress.Type.Primary import com.tangem.features.tokenreceive.route.TokenReceiveRoutes import com.tangem.features.tokenreceive.ui.state.TokenReceiveUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -74,9 +77,9 @@ internal class TokenReceiveModel @Inject constructor( stackNavigation.push(configuration = TokenReceiveRoutes.QrCode(addressId = id)) } - override fun onCopyClick(id: Int) { + override fun onCopyClick(id: Int, source: TokenReceiveCopyActionSource) { val addressToCopy = state.value.addresses[id] ?: return - sendCopyActionAnalytic(addressToCopy) + sendCopyActionAnalytic(addressToCopy, source) clipboardManager.setText(text = addressToCopy.value, isSensitive = true) } @@ -103,17 +106,35 @@ internal class TokenReceiveModel @Inject constructor( } } - private fun mapAddresses(addresses: List): ImmutableMap { + private fun mapAddresses( + addresses: List, + networkName: String, + ): ImmutableMap { + val needUseToLegacyAndDefaultName = addresses.any { it.nameService == ReceiveAddressModel.NameService.Legacy } return buildMap { addresses.mapIndexed { index, model -> val type = when (model.nameService) { ReceiveAddressModel.NameService.Default -> { - ReceiveAddress.Type.Default( - displayName = stringReference(model.displayName), + Primary.Default( + displayName = if (needUseToLegacyAndDefaultName) { + TextReference.Res(R.string.domain_receive_assets_default_address) + } else { + TextReference.Combined( + wrappedList( + TextReference.Str(networkName), + TextReference.Str(" "), + TextReference.Res(R.string.common_address), + ), + ) + }, ) } - ReceiveAddressModel.NameService.Ens -> ReceiveAddress.Type.Ens + ReceiveAddressModel.NameService.Ens -> Ens + ReceiveAddressModel.NameService.Legacy -> Primary.Legacy( + displayName = TextReference.Res(R.string.domain_receive_assets_legacy_address), + ) } + put( key = index, value = ReceiveAddress( @@ -146,10 +167,18 @@ internal class TokenReceiveModel @Inject constructor( val newEnsAddresses = reverseResolveResult .filterIsInstance() .filterNot { it.name in currentAddressValues } - .map { ensAddress -> ReceiveAddress(value = ensAddress.name, type = ReceiveAddress.Type.Ens) } + .map { ensAddress -> ReceiveAddress(value = ensAddress.name, type = Ens) } val combinedAddresses = (state.value.addresses.values + newEnsAddresses) - .sortedWith(compareByDescending { it.type is ReceiveAddress.Type.Ens }) + .sortedWith( + compareBy { address -> + when (address.type) { + is Ens -> 0 + is Primary.Default -> 1 + is Primary.Legacy -> 2 + } + }, + ) val updatedAddresses = combinedAddresses .mapIndexed { index, address -> index to address } @@ -191,7 +220,10 @@ internal class TokenReceiveModel @Inject constructor( private fun getInitState(): TokenReceiveUM { return TokenReceiveUM( - addresses = mapAddresses(params.config.receiveAddress), + addresses = mapAddresses( + addresses = params.config.receiveAddress, + networkName = params.config.cryptoCurrency.network.name, + ), iconState = iconStateConverter.convert(params.config.cryptoCurrency), network = params.config.cryptoCurrency.network.name, isEnsResultLoading = false, @@ -199,15 +231,16 @@ internal class TokenReceiveModel @Inject constructor( ) } - private fun sendCopyActionAnalytic(receiveAddress: ReceiveAddress) { + private fun sendCopyActionAnalytic(receiveAddress: ReceiveAddress, source: TokenReceiveCopyActionSource) { val event = when (receiveAddress.type) { - is ReceiveAddress.Type.Default -> { + is Primary -> { TokenReceiveNewAnalyticsEvent.ButtonCopyAddress( token = getTokenName(), blockchainName = params.config.cryptoCurrency.network.name, + tokenReceiveSource = source, ) } - ReceiveAddress.Type.Ens -> { + Ens -> { TokenReceiveNewAnalyticsEvent.ButtonCopyEns( token = getTokenName(), blockchainName = params.config.cryptoCurrency.network.name, diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt index 68246b663f..92bcd28bc0 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt @@ -5,8 +5,8 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.features.tokenreceive.component.TokenReceiveQrCodeComponent -import com.tangem.features.tokenreceive.entity.ReceiveAddress import com.tangem.features.tokenreceive.ui.state.QrCodeUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow @@ -25,10 +25,15 @@ internal class TokenReceiveQrCodeModel @Inject constructor( internal val state: StateFlow field = MutableStateFlow( QrCodeUM( - network = params.network, + network = params.cryptoCurrency.network.name, addressValue = params.address.value, - addressName = (params.address.type as? ReceiveAddress.Type.Default)?.displayName ?: TextReference.EMPTY, - onCopyClick = { params.callback.onCopyClick(params.id) }, + addressName = TextReference.Str("${params.cryptoCurrency.name} (${params.cryptoCurrency.symbol})"), + onCopyClick = { + params.callback.onCopyClick( + id = params.id, + source = TokenReceiveCopyActionSource.QR, + ) + }, onShareClick = params.callback::onShareClick, ), ) diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveWarningModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveWarningModel.kt index 8c686c07a7..7d57f2d60d 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveWarningModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveWarningModel.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.Stable import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.core.ui.extensions.iconResId import com.tangem.features.tokenreceive.component.TokenReceiveWarningComponent import com.tangem.features.tokenreceive.ui.state.WarningUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -27,7 +26,6 @@ internal class TokenReceiveWarningModel @Inject constructor( iconState = params.iconState, onWarningAcknowledged = params.callback::onWarningAcknowledged, network = params.network.name, - networkIcon = params.network.iconResId, ), ) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt index 10f41e7e05..0bc0d7a0c7 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveAssetsContent.kt @@ -36,6 +36,7 @@ import com.tangem.core.ui.components.atoms.text.TextEllipsis import com.tangem.core.ui.components.buttons.small.TangemIconButton import com.tangem.core.ui.components.icons.identicon.IdentIcon import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe @@ -78,7 +79,6 @@ internal fun TokenReceiveAssetsContent(assetsUM: ReceiveAssetsUM) { onOpenQrCodeClick = assetsUM.onOpenQrCodeClick, addresses = assetsUM.addresses, snackbarHostState = snackbarHostState, - fullName = assetsUM.fullName, ) if (assetsUM.isEnsResultLoading) { @@ -125,7 +125,6 @@ private fun AddressBlock( onCopyClick: (id: Int) -> Unit, addresses: ImmutableMap, snackbarHostState: SnackbarHostState, - fullName: String, ) { val hapticFeedback = LocalHapticFeedback.current val coroutineScope = rememberCoroutineScope() @@ -133,8 +132,8 @@ private fun AddressBlock( val resources = context.resources addresses.entries.toList().fastForEach { entry -> - when (entry.value.type) { - is ReceiveAddress.Type.Default -> { + when (val type = entry.value.type) { + is ReceiveAddress.Type.Primary -> { key(entry.key) { AddressItem( onCopyClick = { @@ -152,8 +151,8 @@ private fun AddressBlock( hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) onOpenQrCodeClick(entry.key) }, - fullName = fullName, address = entry.value.value, + primaryType = type, ) SpacerH8() } @@ -184,9 +183,9 @@ private fun AddressBlock( @Composable private fun AddressItem( onOpenQrCodeClick: () -> Unit, - fullName: String, onCopyClick: () -> Unit, address: String, + primaryType: ReceiveAddress.Type.Primary, modifier: Modifier = Modifier, ) { Card( @@ -195,52 +194,56 @@ private fun AddressItem( colors = CardDefaults.cardColors(containerColor = TangemTheme.colors.background.action), onClick = onOpenQrCodeClick, ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 14.dp, horizontal = 12.dp), - verticalAlignment = Alignment.CenterVertically, + Column( + modifier = Modifier.padding(vertical = 14.dp, horizontal = 12.dp), ) { - IdentIcon( - address = address, - modifier = Modifier - .size(size = 36.dp) - .clip(shape = RoundedCornerShape(18.dp)), - ) - - SpacerW12() - - Column(modifier = Modifier.weight(1f)) { - EllipsisText( - text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, fullName), - ellipsis = TextEllipsis.Middle, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.subtitle1, + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + IdentIcon( + address = address, + modifier = Modifier + .size(size = 36.dp) + .clip(shape = RoundedCornerShape(18.dp)), ) - EllipsisText( - text = address, - ellipsis = TextEllipsis.Middle, - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption2, + SpacerW12() + + Column(modifier = Modifier.weight(1f)) { + EllipsisText( + text = primaryType.displayName.resolveReference(), + ellipsis = TextEllipsis.Middle, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.subtitle1, + ) + + EllipsisText( + text = address, + ellipsis = TextEllipsis.Middle, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption2, + ) + } + + SpacerW12() + + TangemIconButton( + modifier = Modifier.size(TangemTheme.dimens.size28), + innerPadding = 6.dp, + iconRes = R.drawable.ic_qrcode_new_24, + onClick = onOpenQrCodeClick, + ) + + SpacerW8() + + TangemIconButton( + modifier = Modifier.size(TangemTheme.dimens.size28), + iconRes = R.drawable.ic_copy_new_24, + innerPadding = 6.dp, + onClick = onCopyClick, ) } - - SpacerW12() - - TangemIconButton( - modifier = Modifier.size(TangemTheme.dimens.size28), - iconRes = R.drawable.ic_qrcode_new_24, - onClick = onOpenQrCodeClick, - ) - - SpacerW8() - - TangemIconButton( - modifier = Modifier.size(TangemTheme.dimens.size28), - iconRes = R.drawable.ic_copy_new_24, - onClick = onCopyClick, - ) } } } @@ -275,8 +278,9 @@ private fun EnsItem(onCopyClick: () -> Unit, address: String, modifier: Modifier ) TangemIconButton( - modifier = Modifier.size(28.dp), + modifier = Modifier.size(TangemTheme.dimens.size28), iconRes = R.drawable.ic_copy_new_24, + innerPadding = 6.dp, onClick = onCopyClick, ) } @@ -318,7 +322,7 @@ private fun Preview_TokenReceiveAssetsContent( private class TokenReceiveAssetsContentProvider : PreviewParameterProvider { val address = ReceiveAddress( value = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d", - type = ReceiveAddress.Type.Default( + type = ReceiveAddress.Type.Primary.Default( displayName = stringReference("Etherium address"), ), ) @@ -338,7 +342,6 @@ private class TokenReceiveAssetsContentProvider : PreviewParameterProvider diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt index 75ee1fb93f..f4e495de55 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveContent.kt @@ -1,6 +1,7 @@ package com.tangem.features.tokenreceive.ui import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -83,7 +84,7 @@ internal fun TokenReceiveContent( ) { Children( stack = stackState, - animation = stackAnimation(fade()), + animation = stackAnimation(fade(animationSpec = tween(durationMillis = 100))), modifier = modifier .fillMaxSize() .animateContentSize(), diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt index 2aa8aeff9c..25c47e9b3c 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/TokenReceiveWarningContent.kt @@ -3,15 +3,12 @@ package com.tangem.features.tokenreceive.ui import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -21,6 +18,7 @@ import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.SpacerW6 import com.tangem.core.ui.components.currency.icon.CurrencyIcon import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.extensions.stringResourceSafe @@ -46,15 +44,17 @@ internal fun TokenReceiveWarningContent(warningUM: WarningUM) { horizontalAlignment = Alignment.CenterHorizontally, ) { CurrencyIcon( - modifier = Modifier.size(size = 56.dp), + modifier = Modifier + .padding(8.dp) + .size(size = 64.dp), state = warningUM.iconState, - shouldDisplayNetwork = false, + shouldDisplayNetwork = true, iconSize = 56.dp, ) SpacerH24() - WarningBlock(networkIcon = warningUM.networkIcon, networkName = warningUM.network) + WarningBlock(networkName = warningUM.network) SpacerH12() @@ -79,7 +79,7 @@ internal fun TokenReceiveWarningContent(warningUM: WarningUM) { } @Composable -fun WarningBlock(networkName: String, networkIcon: Int, modifier: Modifier = Modifier) { +fun WarningBlock(networkName: String, modifier: Modifier = Modifier) { Column( modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, @@ -91,21 +91,14 @@ fun WarningBlock(networkName: String, networkIcon: Int, modifier: Modifier = Mod color = TangemTheme.colors.text.primary1, ) - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - modifier = Modifier.size(20.dp), - painter = painterResource(id = networkIcon), - tint = Color.Unspecified, - contentDescription = null, - ) + SpacerW6() - Text( - textAlign = TextAlign.Center, - text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, networkName), - style = TangemTheme.typography.h3, - color = TangemTheme.colors.text.primary1, - ) - } + Text( + textAlign = TextAlign.Center, + text = stringResourceSafe(R.string.domain_receive_assets_onboarding_network_name, networkName), + style = TangemTheme.typography.h3, + color = TangemTheme.colors.text.primary1, + ) } } @@ -136,7 +129,6 @@ private class TokenReceiveWarningContentProvider : PreviewParameterProvider Unit, val isEnsResultLoading: Boolean, val notificationConfigs: ImmutableList, - val fullName: String, ) \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt index c31262fd58..4bf0076b45 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/ui/state/WarningUM.kt @@ -4,7 +4,6 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState internal data class WarningUM( val network: String, - val networkIcon: Int, val iconState: CurrencyIconState, val onWarningAcknowledged: () -> Unit, ) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 7f2de4ef2c..d0e4d0f29c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -58,6 +58,8 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource +import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent @@ -876,7 +878,13 @@ internal class TokenDetailsModel @Inject constructor( vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click) clipboardManager.setText(text = defaultAddress, isSensitive = true) - analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol)) + analyticsEventsHandler.send( + TokenReceiveNewAnalyticsEvent.ButtonCopyAddress( + token = cryptoCurrency.symbol, + blockchainName = cryptoCurrency.network.name, + tokenReceiveSource = TokenReceiveCopyActionSource.Token, + ), + ) return resourceReference(R.string.wallet_notification_address_copied) } @@ -1089,16 +1097,17 @@ internal class TokenDetailsModel @Inject constructor( ReceiveAddressModel( nameService = ReceiveAddressModel.NameService.Ens, value = ens, - displayName = ens, ), ) } addresses.availableAddresses.map { address -> add( ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Default, + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, value = address.value, - displayName = "${cryptoCurrency.name} (${cryptoCurrency.symbol})", ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt index 71d5e76c5e..e7876af2a1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt @@ -49,6 +49,8 @@ import com.tangem.domain.tokens.RemoveCurrencyUseCase import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource +import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.AVAILABLE import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText @@ -174,10 +176,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( ), ) - analyticsEventHandler.send( - event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrencyStatus.currency.symbol), - ) - event?.let { analyticsEventHandler.send(it) } if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) { @@ -188,6 +186,9 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( } } } else { + analyticsEventHandler.send( + event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrencyStatus.currency.symbol), + ) stateHolder.showBottomSheet( createReceiveBottomSheetContent( currency = cryptoCurrencyStatus.currency, @@ -206,7 +207,13 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click) clipboardManager.setText(text = defaultAddress, isSensitive = true) - analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol)) + analyticsEventHandler.send( + TokenReceiveNewAnalyticsEvent.ButtonCopyAddress( + token = cryptoCurrency.symbol, + blockchainName = cryptoCurrency.network.name, + tokenReceiveSource = TokenReceiveCopyActionSource.Main, + ), + ) return resourceReference(R.string.wallet_notification_address_copied) } @@ -235,7 +242,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( override fun onCopyAddressClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { analyticsEventHandler.send( - event = TokenScreenAnalyticsEvent.ButtonCopyAddress(cryptoCurrencyStatus.currency.symbol), + event = TokenReceiveNewAnalyticsEvent.ButtonCopyAddress( + token = cryptoCurrencyStatus.currency.symbol, + blockchainName = cryptoCurrencyStatus.currency.network.name, + tokenReceiveSource = TokenReceiveCopyActionSource.Main, + ), ) modelScope.launch(dispatchers.main) { @@ -673,16 +684,17 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( ReceiveAddressModel( nameService = ReceiveAddressModel.NameService.Ens, value = ens, - displayName = ens, ), ) } networkAddress.availableAddresses.map { address -> add( ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Default, + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, value = address.value, - displayName = "${cryptoCurrencyStatus.currency.name} (${cryptoCurrencyStatus.currency.symbol})", ), ) } diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt index b5f412ebce..29a42feb84 100644 --- a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt @@ -1,3 +1,5 @@ +@file:Suppress("FunctionSignature") + package com.tangem.feature.wallet.presentation.wallet.deeplink import arrow.core.Either @@ -354,80 +356,82 @@ class DefaultPromoDeeplinkHandlerTest { } @Test - fun `GIVEN BTC status but currencies without BTC WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to "PROMO123") - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val btcStatus = buildNetworkStatus(rawNetworkId = Blockchain.Bitcoin.id, address = "bc1qxyz") - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatus)) - val ethOnly = buildCryptoCurrency(rawNetworkId = "ethereum") - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(ethOnly) - val dispatcherProvider = testDispatcherProvider(testScheduler) + fun `GIVEN BTC status but currencies without BTC WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to "PROMO123") + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + val btcStatus = buildNetworkStatus(rawNetworkId = Blockchain.Bitcoin.id, address = "bc1qxyz") + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatus)) + val ethOnly = buildCryptoCurrency(rawNetworkId = "ethereum") + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(ethOnly) + val dispatcherProvider = testDispatcherProvider(testScheduler) - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) - advanceUntilIdle() + advanceUntilIdle() - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - } + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + } @Test - fun `GIVEN multiple statuses emissions and currencies without BTC WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN multiple statuses emissions and currencies without BTC WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val ethStatus = buildNetworkStatus(rawNetworkId = "ethereum", address = "0x123") - val btcStatus = buildNetworkStatus(rawNetworkId = Blockchain.Bitcoin.id, address = "bc1qxyz") - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flow { - emit(emptySet()) - emit(setOf(ethStatus)) - emit(setOf(ethStatus, btcStatus)) - } + val ethStatus = buildNetworkStatus(rawNetworkId = "ethereum", address = "0x123") + val btcStatus = buildNetworkStatus(rawNetworkId = Blockchain.Bitcoin.id, address = "bc1qxyz") + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flow { + emit(emptySet()) + emit(setOf(ethStatus)) + emit(setOf(ethStatus, btcStatus)) + } - val ethOnly = buildCryptoCurrency(rawNetworkId = "ethereum") - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(ethOnly) + val ethOnly = buildCryptoCurrency(rawNetworkId = "ethereum") + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(ethOnly) - val dispatcherProvider = testDispatcherProvider(testScheduler) + val dispatcherProvider = testDispatcherProvider(testScheduler) - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + ) + } } - } @Test fun `GIVEN two BTC statuses with different derivation AND two BTC currencies WHEN activate THEN activated`() = @@ -485,270 +489,275 @@ class DefaultPromoDeeplinkHandlerTest { } @Test - fun `GIVEN two BTC statuses with different derivation AND one matching BTC currency WHEN activate THEN activated`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN two BTC statuses with different derivation AND one matching BTC currency WHEN activate THEN activated`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") - val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") + val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") + val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") - val btcStatusCard = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcard", - derivationPath = dpCard, - ) - val btcStatusCustom = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcustom", - derivationPath = dpCustom, - ) - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard, btcStatusCustom)) - - val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCard) - - coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) } returns Either.Right("ok") - - val dispatcherProvider = testDispatcherProvider(testScheduler) - - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success)) - - coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) } - coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) } - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Activated), + val btcStatusCard = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcard", + derivationPath = dpCard, ) + val btcStatusCustom = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcustom", + derivationPath = dpCustom, + ) + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard, btcStatusCustom)) + + val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCard) + + coEvery { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) } returns Either.Right("ok") + + val dispatcherProvider = testDispatcherProvider(testScheduler) + + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_activation_success)) + + coVerify(exactly = 1) { activateBitcoinPromocodeUseCase.invoke("bc1qcard", promoCode) } + coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke("bc1qcustom", promoCode) } + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.Activated), + ) + } } - } @Test - fun `GIVEN two BTC statuses with different derivation AND no BTC currencies WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN two BTC statuses with different derivation AND no BTC currencies WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") - val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") + val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") + val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") - val btcStatusCard = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcard", - derivationPath = dpCard, - ) - val btcStatusCustom = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcustom", - derivationPath = dpCustom, - ) - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard, btcStatusCustom)) - - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns emptySet() - - val dispatcherProvider = testDispatcherProvider(testScheduler) - - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - - coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + val btcStatusCard = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcard", + derivationPath = dpCard, ) + val btcStatusCustom = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcustom", + derivationPath = dpCustom, + ) + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard, btcStatusCustom)) + + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns emptySet() + + val dispatcherProvider = testDispatcherProvider(testScheduler) + + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + + coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + ) + } } - } @Test - fun `GIVEN two BTC currencies first derivation mismatched AND one matching status WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN two BTC currencies first derivation mismatched AND one matching status WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") - val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") + val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") + val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") - val btcStatusCard = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcard", - derivationPath = dpCard, - ) - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard)) - - val btcCoinCustom = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCustom) - val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf( - btcCoinCustom, - btcCoinCard, - ) - - val dispatcherProvider = testDispatcherProvider(testScheduler) - - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - - coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + val btcStatusCard = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcard", + derivationPath = dpCard, ) + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard)) + + val btcCoinCustom = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCustom) + val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf( + btcCoinCustom, + btcCoinCard, + ) + + val dispatcherProvider = testDispatcherProvider(testScheduler) + + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + + coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + ) + } } - } @Test - fun `GIVEN only BTC status with CUSTOM derivation AND only BTC currency with CARD derivation WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN only BTC status with CUSTOM derivation AND only BTC currency with CARD derivation WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") - val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") + val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") + val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") - val btcStatusCustom = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcustom", - derivationPath = dpCustom, - ) - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCustom)) - - val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCard) - - val dispatcherProvider = testDispatcherProvider(testScheduler) - - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - - coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + val btcStatusCustom = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcustom", + derivationPath = dpCustom, ) + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCustom)) + + val btcCoinCard = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCard) + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCard) + + val dispatcherProvider = testDispatcherProvider(testScheduler) + + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + + coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + ) + } } - } @Test - fun `GIVEN only BTC status with CARD derivation AND only BTC currency with CUSTOM derivation WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = runTest { - val promoCode = "PROMO123" - val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) - val userWallet = mockUserWallet("ABCDEF") - every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) + fun `GIVEN only BTC status with CARD derivation AND only BTC currency with CUSTOM derivation WHEN findBitcoinAddress THEN no bitcoin address dialog is shown`() = + runTest { + val promoCode = "PROMO123" + val queryParams = mapOf(DeeplinkConst.PROMO_CODE_KEY to promoCode) + val userWallet = mockUserWallet("ABCDEF") + every { getSelectedWalletSyncUseCase.invoke() } returns Either.Right(userWallet) - val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") - val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") + val dpCard = Network.DerivationPath.Card("m/44'/0'/0'") + val dpCustom = Network.DerivationPath.Custom("m/84'/0'/0'") - val btcStatusCard = buildNetworkStatus( - rawNetworkId = Blockchain.Bitcoin.id, - address = "bc1qcard", - derivationPath = dpCard, - ) - coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard)) - - val btcCoinCustom = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCustom) - coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCustom) - - val dispatcherProvider = testDispatcherProvider(testScheduler) - - DefaultPromoDeeplinkHandler( - scope = this, - queryParams = queryParams, - uiMessageSender = uiMessageSender, - multiNetworkStatusSupplier = multiNetworkStatusSupplier, - multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, - activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - analyticsEventsHandler = analyticsEventHandler, - dispatchers = dispatcherProvider, - ) - - advanceUntilIdle() - - val sent = messages.last { it is DialogMessage } as DialogMessage - Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) - Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) - - coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } - - verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } - verify(exactly = 1) { - analyticsEventHandler.send( - PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + val btcStatusCard = buildNetworkStatus( + rawNetworkId = Blockchain.Bitcoin.id, + address = "bc1qcard", + derivationPath = dpCard, ) + coEvery { multiNetworkStatusSupplier.invoke(any()) } returns flowOf(setOf(btcStatusCard)) + + val btcCoinCustom = buildCryptoCurrency(rawNetworkId = Blockchain.Bitcoin.id, derivationPath = dpCustom) + coEvery { multiWalletCryptoCurrenciesSupplier.getSyncOrNull(any()) } returns setOf(btcCoinCustom) + + val dispatcherProvider = testDispatcherProvider(testScheduler) + + DefaultPromoDeeplinkHandler( + scope = this, + queryParams = queryParams, + uiMessageSender = uiMessageSender, + multiNetworkStatusSupplier = multiNetworkStatusSupplier, + multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier, + activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, + getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + analyticsEventsHandler = analyticsEventHandler, + dispatchers = dispatcherProvider, + ) + + advanceUntilIdle() + + val sent = messages.last { it is DialogMessage } as DialogMessage + Truth.assertThat(sent.title).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address_title)) + Truth.assertThat(sent.message).isEqualTo(resourceReference(R.string.bitcoin_promo_no_address)) + + coVerify(exactly = 0) { activateBitcoinPromocodeUseCase.invoke(any(), any()) } + + verify(exactly = 1) { analyticsEventHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart) } + verify(exactly = 1) { + analyticsEventHandler.send( + PromoActivationAnalytics.PromoActivation(PromoCodeActivationResult.NoBitcoinAddress), + ) + } } - } private fun mockUserWallet(id: String): UserWallet { val userWallet = mockk(relaxed = true) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index c57f7b4239..33e667f9d0 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -5,9 +5,9 @@ # https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/vico -tangemBlockchainSdk = "develop-1205" +tangemBlockchainSdk = "develop-1212" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-557" +tangemCardSdk = "develop-560" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem12" #tangemVico = "0.0.1" # Keep it! - used for local builds ^ diff --git a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt index 34d846e753..0449da0454 100644 --- a/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt +++ b/libs/blockchain-sdk/src/main/java/com/tangem/blockchainsdk/utils/Blockchain.kt @@ -165,8 +165,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "zklink/test" -> Blockchain.ZkLinkNovaTestnet "pepecoin" -> Blockchain.Pepecoin "pepecoin/test" -> Blockchain.PepecoinTestnet - "hyperliquid" -> Blockchain.Hyperliquid - "hyperliquid/test" -> Blockchain.HyperliquidTestnet + "hyperevm" -> Blockchain.Hyperliquid + "hyperevm/test" -> Blockchain.HyperliquidTestnet else -> null } } @@ -329,8 +329,8 @@ fun Blockchain.toNetworkId(): String { Blockchain.ZkLinkNovaTestnet -> "zklink/test" Blockchain.Pepecoin -> "pepecoin" Blockchain.PepecoinTestnet -> "pepecoin/test" - Blockchain.Hyperliquid -> "hyperliquid" - Blockchain.HyperliquidTestnet -> "hyperliquid/test" + Blockchain.Hyperliquid -> "hyperevm" + Blockchain.HyperliquidTestnet -> "hyperevm/test" } }