From 8c9452f46c013d7d6bf66cf36c6a8624c9f51ee1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Oct 2025 23:15:22 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TransactionDomainModule.kt | 13 ++ .../usecase/ReceiveAddressesFactory.kt | 124 +++++++++++ .../archived/di/AccountArchivedModule.kt | 11 +- .../createedit/di/AccountCreateEditModule.kt | 11 +- .../details/di/AccountDetailsModule.kt | 11 +- .../account/di/AccountFeatureModule.kt | 21 ++ .../add/impl/TokenActionsComponent.kt | 79 +++++++ .../add/impl/model/TokenActionsModel.kt | 80 +++++++ .../add/impl/model/TokenActionsUiBuilder.kt | 39 ++++ .../add/impl/ui/TokenActionsContent.kt | 204 ++++++++++++++++++ .../add/impl/ui/state/TokenActionsUM.kt | 10 + .../impl/model/MarketsPortfolioModel.kt | 62 ++---- .../impl/model/PortfolioTokenUMConverter.kt | 99 +++++---- .../nft/receive/model/NFTReceiveModel.kt | 59 +---- .../referral/model/AccountAwardConverter.kt | 4 +- .../tokendetails/model/TokenDetailsModel.kt | 60 ++---- .../WalletCurrencyActionsClickIntents.kt | 44 +--- 17 files changed, 665 insertions(+), 266 deletions(-) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ReceiveAddressesFactory.kt create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/TokenActionsComponent.kt create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsUiBuilder.kt create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/TokenActionsContent.kt create mode 100644 features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/state/TokenActionsUM.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index 64dbe9c954..887376f2a3 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -5,6 +5,7 @@ import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.networks.single.SingleNetworkStatusFetcher import com.tangem.domain.networks.single.SingleNetworkStatusSupplier +import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.transaction.TransactionRepository @@ -231,6 +232,18 @@ internal object TransactionDomainModule { ) } + @Provides + @Singleton + fun provideReceiveAddressesFactory( + getEnsNameUseCase: GetEnsNameUseCase, + getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, + ): ReceiveAddressesFactory { + return ReceiveAddressesFactory( + getEnsNameUseCase = getEnsNameUseCase, + getViewedTokenReceiveWarningUseCase = getViewedTokenReceiveWarningUseCase, + ) + } + @Provides @Singleton fun provideGetReverseResolvedEnsAddressUseCase( diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ReceiveAddressesFactory.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ReceiveAddressesFactory.kt new file mode 100644 index 0000000000..e0b596f81e --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ReceiveAddressesFactory.kt @@ -0,0 +1,124 @@ +package com.tangem.domain.transaction.usecase + +import com.tangem.domain.models.Asset +import com.tangem.domain.models.ReceiveAddressModel +import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.domain.models.TokenReceiveNotification +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.network.Network +import com.tangem.domain.models.network.NetworkAddress +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase +import com.tangem.domain.transaction.R +import com.tangem.lib.crypto.BlockchainUtils + +class ReceiveAddressesFactory( + private val getEnsNameUseCase: GetEnsNameUseCase, + private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, +) { + + suspend fun create( + status: CryptoCurrencyStatus, + userWalletId: UserWalletId, + notifications: List = emptyList(), + ): TokenReceiveConfig? { + val addresses = status.value.networkAddress ?: return null + val cryptoCurrency = status.currency + + val ensName = getEnsNameUseCase.invoke( + userWalletId = userWalletId, + network = cryptoCurrency.network, + address = addresses.defaultAddress.value, + ) + + val receiveAddresses = buildList { + ensName?.let { ens -> + add( + ReceiveAddressModel( + nameService = ReceiveAddressModel.NameService.Ens, + value = ens, + ), + ) + } + addresses.availableAddresses.map { address -> + add( + ReceiveAddressModel( + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, + value = address.value, + ), + ) + } + } + return TokenReceiveConfig( + shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(), + cryptoCurrency = cryptoCurrency, + userWalletId = userWalletId, + showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network + .TransactionExtrasType.NONE, + receiveAddress = receiveAddresses, + tokenReceiveNotification = notifications, + ) + } + + suspend fun createForNft( + userWalletId: UserWalletId, + addresses: NetworkAddress, + network: Network, + nft: CryptoCurrency, + ): TokenReceiveConfig { + val cryptoCurrency = nft + + val ensName = getEnsNameUseCase.invoke( + userWalletId = userWalletId, + network = network, + address = addresses.defaultAddress.value, + ) + + val receiveAddresses = buildList { + ensName?.let { ens -> + add( + ReceiveAddressModel( + nameService = ReceiveAddressModel.NameService.Ens, + value = ens, + ), + ) + } + addresses.availableAddresses.map { address -> + add( + ReceiveAddressModel( + nameService = when (address.type) { + NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default + NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy + }, + value = address.value, + ), + ) + } + } + + val notifications = buildList { + if (BlockchainUtils.isSolana(network.rawId)) { + add( + TokenReceiveNotification( + title = R.string.nft_receive_unsupported_types, + subtitle = R.string.nft_receive_unsupported_types_description, + ), + ) + } + } + + return TokenReceiveConfig( + shouldShowWarning = Asset.NFT.name !in getViewedTokenReceiveWarningUseCase(), + cryptoCurrency = cryptoCurrency, + userWalletId = userWalletId, + showMemoDisclaimer = false, + receiveAddress = receiveAddresses, + tokenReceiveNotification = notifications, + asset = Asset.NFT, + ) + } +} \ No newline at end of file diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/di/AccountArchivedModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/di/AccountArchivedModule.kt index 21c674cef2..a853d04fe1 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/archived/di/AccountArchivedModule.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/di/AccountArchivedModule.kt @@ -1,25 +1,18 @@ package com.tangem.features.account.archived.di +import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model -import com.tangem.features.account.ArchivedAccountListComponent import com.tangem.features.account.archived.ArchivedAccountListModel -import com.tangem.features.account.archived.DefaultArchivedAccountListComponent import dagger.Binds import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module -@InstallIn(SingletonComponent::class) +@InstallIn(ModelComponent::class) internal interface AccountArchivedModule { - @Binds - fun bindArchivedAccountListComponentFactory( - impl: DefaultArchivedAccountListComponent.Factory, - ): ArchivedAccountListComponent.Factory - @Binds @IntoMap @ClassKey(ArchivedAccountListModel::class) diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt index ab899b3f5c..3c85af74ee 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/di/AccountCreateEditModule.kt @@ -1,25 +1,18 @@ package com.tangem.features.account.createedit.di +import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model -import com.tangem.features.account.AccountCreateEditComponent import com.tangem.features.account.createedit.AccountCreateEditModel -import com.tangem.features.account.createedit.DefaultAccountCreateEditComponent import dagger.Binds import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module -@InstallIn(SingletonComponent::class) +@InstallIn(ModelComponent::class) internal interface AccountCreateEditModule { - @Binds - fun bindAccountCreateEditComponentFactory( - impl: DefaultAccountCreateEditComponent.Factory, - ): AccountCreateEditComponent.Factory - @Binds @IntoMap @ClassKey(AccountCreateEditModel::class) diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/di/AccountDetailsModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/di/AccountDetailsModule.kt index fdc4feda8a..7975787c3e 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/details/di/AccountDetailsModule.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/details/di/AccountDetailsModule.kt @@ -1,25 +1,18 @@ package com.tangem.features.account.details.di +import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model -import com.tangem.features.account.AccountDetailsComponent import com.tangem.features.account.details.AccountDetailsModel -import com.tangem.features.account.details.DefaultAccountDetailsComponent import dagger.Binds import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap @Module -@InstallIn(SingletonComponent::class) +@InstallIn(ModelComponent::class) internal interface AccountDetailsModule { - @Binds - fun bindAccountDetailsComponentFactory( - impl: DefaultAccountDetailsComponent.Factory, - ): AccountDetailsComponent.Factory - @Binds @IntoMap @ClassKey(AccountDetailsModel::class) diff --git a/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt index 67ce3bbd1a..9f77e55922 100644 --- a/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt +++ b/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt @@ -1,8 +1,14 @@ package com.tangem.features.account.di +import com.tangem.features.account.AccountCreateEditComponent +import com.tangem.features.account.AccountDetailsComponent +import com.tangem.features.account.ArchivedAccountListComponent import com.tangem.features.account.PortfolioFetcher import com.tangem.features.account.PortfolioSelectorComponent import com.tangem.features.account.PortfolioSelectorController +import com.tangem.features.account.archived.DefaultArchivedAccountListComponent +import com.tangem.features.account.createedit.DefaultAccountCreateEditComponent +import com.tangem.features.account.details.DefaultAccountDetailsComponent import com.tangem.features.account.fetcher.DefaultPortfolioFetcher import com.tangem.features.account.selector.DefaultPortfolioSelectorComponent import com.tangem.features.account.selector.DefaultPortfolioSelectorController @@ -25,4 +31,19 @@ internal interface AccountFeatureModule { fun bindPortfolioSelectorComponentFactory( impl: DefaultPortfolioSelectorComponent.Factory, ): PortfolioSelectorComponent.Factory + + @Binds + fun bindAccountCreateEditComponentFactory( + impl: DefaultAccountCreateEditComponent.Factory, + ): AccountCreateEditComponent.Factory + + @Binds + fun bindAccountDetailsComponentFactory( + impl: DefaultAccountDetailsComponent.Factory, + ): AccountDetailsComponent.Factory + + @Binds + fun bindArchivedAccountListComponentFactory( + impl: DefaultArchivedAccountListComponent.Factory, + ): ArchivedAccountListComponent.Factory } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/TokenActionsComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/TokenActionsComponent.kt new file mode 100644 index 0000000000..93bf26a76f --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/TokenActionsComponent.kt @@ -0,0 +1,79 @@ +package com.tangem.features.markets.portfolio.add.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.features.markets.portfolio.add.impl.model.TokenActionsModel +import com.tangem.features.markets.portfolio.add.impl.ui.TokenActionsContent +import com.tangem.features.markets.portfolio.impl.analytics.PortfolioAnalyticsEvent +import com.tangem.features.markets.portfolio.impl.loader.PortfolioData +import com.tangem.features.tokenreceive.TokenReceiveComponent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlinx.coroutines.flow.Flow + +internal class TokenActionsComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + @Assisted private val params: Params, + private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory, +) : AppComponentContext by context, ComposableContentComponent { + + private val model: TokenActionsModel = getOrCreateModel(params) + private val bottomSheetSlot = childSlot( + source = model.bottomSheetNavigation, + serializer = TokenReceiveConfig.serializer(), + handleBackButton = false, + childFactory = ::bottomSheetChild, + ) + + @Composable + override fun Content(modifier: Modifier) { + val state = model.uiState.collectAsStateWithLifecycle() + val bottomSheet by bottomSheetSlot.subscribeAsState() + val tokenActionsUM = state.value ?: return + TokenActionsContent( + modifier = modifier, + state = tokenActionsUM, + ) + bottomSheet.child?.instance?.BottomSheet() + } + + private fun bottomSheetChild( + config: TokenReceiveConfig, + componentContext: ComponentContext, + ): ComposableBottomSheetComponent = tokenReceiveComponentFactory.create( + context = childByContext(componentContext), + params = TokenReceiveComponent.Params( + config = config, + onDismiss = model.bottomSheetNavigation::dismiss, + ), + ) + + data class Params( + val eventBuilder: PortfolioAnalyticsEvent.EventBuilder, + val data: Flow, + val callbacks: Callbacks, + ) + + interface Callbacks { + fun onLaterClick() + } + + @AssistedFactory + interface Factory : ComponentFactory { + override fun create(context: AppComponentContext, params: Params): TokenActionsComponent + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt new file mode 100644 index 0000000000..a2d0ceeba2 --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt @@ -0,0 +1,80 @@ +package com.tangem.features.markets.portfolio.add.impl.model + +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.tangem.core.analytics.api.AnalyticsEventHandler +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.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory +import com.tangem.features.markets.portfolio.add.impl.TokenActionsComponent +import com.tangem.features.markets.portfolio.add.impl.ui.state.TokenActionsUM +import com.tangem.features.markets.portfolio.impl.model.TokenActionsHandler +import com.tangem.features.markets.portfolio.impl.model.TokenActionsHandler.HandledQuickAction +import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM +import com.tangem.utils.Provider +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ModelScoped +@Suppress("LongParameterList") +internal class TokenActionsModel @Inject constructor( + paramsContainer: ParamsContainer, + getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + tokenActionsIntentsFactory: TokenActionsHandler.Factory, + override val dispatchers: CoroutineDispatcherProvider, + private val uiBuilder: TokenActionsUiBuilder, + private val analyticsEventHandler: AnalyticsEventHandler, + private val receiveAddressesFactory: ReceiveAddressesFactory, +) : Model() { + + private val params = paramsContainer.require() + private val analyticsEventBuilder get() = params.eventBuilder + private val currentAppCurrency = getSelectedAppCurrencyUseCase.invokeOrDefault() + .stateIn( + scope = modelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) + + private val tokenActionsHandler: TokenActionsHandler = + tokenActionsIntentsFactory.create( + currentAppCurrency = Provider { currentAppCurrency.value }, + updateTokenReceiveBSConfig = { }, + onHandleQuickAction = { handledAction -> handledQuickAction(handledAction) }, + ) + + val bottomSheetNavigation: SlotNavigation = SlotNavigation() + val uiState: StateFlow = params.data + .mapLatest { uiBuilder.build(it, tokenActionsHandler) } + .stateIn( + scope = modelScope, + started = SharingStarted.Eagerly, + initialValue = null, + ) + + private fun handledQuickAction(handledAction: HandledQuickAction) { + val event = analyticsEventBuilder.quickActionClick( + actionUM = handledAction.action, + blockchainName = handledAction.cryptoCurrencyData.status.currency.network.name, + ) + analyticsEventHandler.send(event) + val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive + if (!isReceive) return + modelScope.launch { + val tokenConfig = receiveAddressesFactory.create( + status = handledAction.cryptoCurrencyData.status, + userWalletId = handledAction.cryptoCurrencyData.userWallet.walletId, + ) ?: return@launch + bottomSheetNavigation.activate(tokenConfig) + } + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsUiBuilder.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsUiBuilder.kt new file mode 100644 index 0000000000..be2f06ccdf --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsUiBuilder.kt @@ -0,0 +1,39 @@ +package com.tangem.features.markets.portfolio.add.impl.model + +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.markets.portfolio.add.impl.TokenActionsComponent +import com.tangem.features.markets.portfolio.add.impl.ui.state.TokenActionsUM +import com.tangem.features.markets.portfolio.impl.loader.PortfolioData +import com.tangem.features.markets.portfolio.impl.model.PortfolioTokenUMConverter +import com.tangem.features.markets.portfolio.impl.model.TokenActionsHandler +import javax.inject.Inject + +@ModelScoped +internal class TokenActionsUiBuilder @Inject constructor( + paramsContainer: ParamsContainer, +) { + private val params = paramsContainer.require() + + fun build(data: PortfolioData.CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler): TokenActionsUM { + val status = data.status + val tokenUM = TokenItemState.Content( + id = status.currency.id.value, + iconState = CryptoCurrencyToIconStateConverter().convert(status.currency), + titleState = TokenItemState.TitleState.Content(stringReference(status.currency.name)), + fiatAmountState = null, + subtitle2State = null, + subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(status.currency.symbol)), + onItemClick = null, + onItemLongClick = null, + ) + return TokenActionsUM( + token = tokenUM, + onLaterClick = { params.callbacks.onLaterClick() }, + quickActions = PortfolioTokenUMConverter.quickActions(data, tokenActionsHandler), + ) + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/TokenActionsContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/TokenActionsContent.kt new file mode 100644 index 0000000000..97e8645655 --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/TokenActionsContent.kt @@ -0,0 +1,204 @@ +package com.tangem.features.markets.portfolio.add.impl.ui + +import android.content.res.Configuration +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.key +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.components.SpacerH16 +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.icons.badge.drawBadge +import com.tangem.core.ui.components.token.TokenItem +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.haptic.TangemHapticEffect +import com.tangem.core.ui.res.LocalHapticManager +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.markets.impl.R +import com.tangem.features.markets.portfolio.add.impl.ui.state.TokenActionsUM +import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM +import com.tangem.features.markets.portfolio.impl.ui.state.QuickActionUM +import kotlinx.collections.immutable.persistentListOf +import java.util.UUID + +@Composable +internal fun TokenActionsContent(state: TokenActionsUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier.fillMaxWidth(), + ) { + TokenItem( + modifier = Modifier + .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) + .background(color = TangemTheme.colors.background.action), + state = state.token, + isBalanceHidden = false, + ) + + SpacerH(TangemTheme.dimens.spacing14) + Column( + modifier = Modifier + .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) + .background(TangemTheme.colors.background.action), + ) { + state.quickActions.actions.fastForEach { + key(it.title) { + ActionRow( + state = it, + onClick = { state.quickActions.onQuickActionClick(it) }, + onLongClick = { state.quickActions.onQuickActionLongClick(it) }, + ) + } + } + } + + SpacerH16() + + SecondaryButton( + modifier = modifier.fillMaxWidth(), + text = stringResourceSafe(R.string.common_later), + onClick = state.onLaterClick, + ) + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ActionRow( + state: QuickActionUM, + onClick: () -> Unit, + onLongClick: (() -> Unit), + modifier: Modifier = Modifier, +) { + val hapticManager = LocalHapticManager.current + val onLongClickInternal = { + hapticManager.perform(TangemHapticEffect.View.LongPress) + onLongClick() + } + + Row( + modifier = modifier + .fillMaxWidth() + .combinedClickable( + onLongClick = onLongClickInternal.takeIf { state.longClickAvailable }, + onClick = { + hapticManager.perform(TangemHapticEffect.View.SegmentTick) + onClick() + }, + ) + .padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing15), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ) { + val containerColor = TangemTheme.colors.background.action + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .background( + color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f), + shape = CircleShape, + ) + .size(36.dp) + .drawWithContent { + drawContent() + if (state is QuickActionUM.Exchange && state.showBadge) { + drawBadge(containerColor = containerColor, offset = 4.dp) + } + }, + ) { + Icon( + modifier = Modifier.requiredSize(TangemTheme.dimens.size16), + imageVector = ImageVector.vectorResource(id = state.icon), + contentDescription = null, + tint = TangemTheme.colors.icon.accent, + ) + } + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2), + ) { + Text( + text = state.title.resolveReference(), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = state.description.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } +} + +@Preview(widthDp = 360, showBackground = true) +@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview(@PreviewParameter(TokenActionsContentPreviewProvider::class) state: TokenActionsUM) { + TangemThemePreview { + TokenActionsContent( + state = state, + ) + } +} + +private class TokenActionsContentPreviewProvider : PreviewParameterProvider { + private val tokenState + get() = TokenItemState.Content( + id = UUID.randomUUID().toString(), + iconState = CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = R.drawable.img_eth_22, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + titleState = TokenItemState.TitleState.Content( + text = stringReference(value = "Tether"), + ), + fiatAmountState = null, + subtitle2State = null, + subtitleState = TokenItemState.SubtitleState.TextContent(value = stringReference("USDT")), + onItemClick = {}, + onItemLongClick = {}, + ) + + override val values: Sequence + get() = sequenceOf( + TokenActionsUM( + quickActions = PortfolioTokenUM.QuickActions( + actions = persistentListOf( + QuickActionUM.Buy, + QuickActionUM.Exchange(showBadge = true), + QuickActionUM.Receive, + ), + onQuickActionClick = {}, + onQuickActionLongClick = {}, + ), + token = tokenState, + onLaterClick = {}, + ), + ) +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/state/TokenActionsUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/state/TokenActionsUM.kt new file mode 100644 index 0000000000..cb2466e02a --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/ui/state/TokenActionsUM.kt @@ -0,0 +1,10 @@ +package com.tangem.features.markets.portfolio.add.impl.ui.state + +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM + +internal data class TokenActionsUM( + val token: TokenItemState, + val quickActions: PortfolioTokenUM.QuickActions, + val onLaterClick: () -> Unit, +) \ No newline at end of file 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 913963f668..3b1183b867 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 @@ -2,9 +2,9 @@ package com.tangem.features.markets.portfolio.impl.model import androidx.compose.runtime.Stable import arrow.core.getOrElse -import com.tangem.common.ui.userwallet.state.UserWalletItemUM import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.common.ui.userwallet.state.UserWalletItemUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -22,16 +22,13 @@ import com.tangem.domain.managetokens.CheckCurrencyUnsupportedUseCase import com.tangem.domain.managetokens.model.CurrencyUnsupportedState import com.tangem.domain.markets.SaveMarketTokensUseCase import com.tangem.domain.markets.TokenMarketInfo -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 -import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase -import com.tangem.domain.transaction.usecase.GetEnsNameUseCase +import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.features.markets.impl.R @@ -70,9 +67,8 @@ internal class MarketsPortfolioModel @Inject constructor( private val addToPortfolioManager: AddToPortfolioManager, private val analyticsEventHandler: AnalyticsEventHandler, private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle, - private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, - private val getEnsNameUseCase: GetEnsNameUseCase, private val userWalletImageFetcher: UserWalletImageFetcher, + private val receiveAddressesFactory: ReceiveAddressesFactory, ) : Model() { val state: StateFlow get() = _state @@ -374,50 +370,16 @@ internal class MarketsPortfolioModel @Inject constructor( } private fun configureReceiveAddresses(quickAction: TokenActionsHandler.HandledQuickAction) { - when (quickAction.action) { - TokenActionsBSContentUM.Action.Receive -> { - val addresses = quickAction.cryptoCurrencyData.status.value.networkAddress ?: return - val cryptoCurrency = quickAction.cryptoCurrencyData.status.currency - modelScope.launch { - val ensName = getEnsNameUseCase.invoke( - userWalletId = quickAction.cryptoCurrencyData.userWallet.walletId, - network = cryptoCurrency.network, - address = addresses.defaultAddress.value, - ) - - val receiveAddresses = buildList { - ensName?.let { ens -> - add( - ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Ens, - value = ens, - ), - ) - } - addresses.availableAddresses.map { address -> - add( - ReceiveAddressModel( - nameService = when (address.type) { - NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default - NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy - }, - value = address.value, - ), - ) - } - } - val tokenConfig = TokenReceiveConfig( - shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(), - cryptoCurrency = cryptoCurrency, - userWalletId = quickAction.cryptoCurrencyData.userWallet.walletId, - showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network - .TransactionExtrasType.NONE, - receiveAddress = receiveAddresses, - ) - bottomSheetNavigation.activate(tokenConfig) - } + val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive && + tokenReceiveFeatureToggle.isNewTokenReceiveEnabled + if (isNewReceive) { + modelScope.launch { + val tokenConfig = receiveAddressesFactory.create( + status = quickAction.cryptoCurrencyData.status, + userWalletId = quickAction.cryptoCurrencyData.userWallet.walletId, + ) ?: return@launch + bottomSheetNavigation.activate(tokenConfig) } - else -> Unit } } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt index e1acbcf4e1..2bb27f2a78 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/PortfolioTokenUMConverter.kt @@ -42,55 +42,60 @@ internal class PortfolioTokenUMConverter( walletId = value.userWallet.walletId, isBalanceHidden = isBalanceHidden, isQuickActionsShown = false, - quickActions = quickActions(cryptoData = value), + quickActions = quickActions(cryptoData = value, tokenActionsHandler = tokenActionsHandler), ) } - private fun quickActions(cryptoData: PortfolioData.CryptoCurrencyData): PortfolioTokenUM.QuickActions { - return PortfolioTokenUM.QuickActions( - actions = toQuickActions(cryptoData.actions), - onQuickActionClick = { - when (it) { - QuickActionUM.Buy -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Buy, - cryptoCurrencyData = cryptoData, - ) - is QuickActionUM.Exchange -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Exchange, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.Receive -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Receive, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.Stake -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Stake, - cryptoCurrencyData = cryptoData, - ) - } - }, - onQuickActionLongClick = { - if (it == QuickActionUM.Receive) { - tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.CopyAddress, - cryptoCurrencyData = cryptoData, - ) - } - }, - ) - } - - private fun toQuickActions(actions: List) = buildList { - actions.forEach { action -> - if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { - when (action) { - is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy - is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(showBadge = action.showBadge) - is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive - is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake - else -> null - }?.let(::add) - } + companion object { + fun quickActions( + cryptoData: PortfolioData.CryptoCurrencyData, + tokenActionsHandler: TokenActionsHandler, + ): PortfolioTokenUM.QuickActions { + return PortfolioTokenUM.QuickActions( + actions = toQuickActions(cryptoData.actions), + onQuickActionClick = { + when (it) { + QuickActionUM.Buy -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Buy, + cryptoCurrencyData = cryptoData, + ) + is QuickActionUM.Exchange -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.Receive -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Receive, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.Stake -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Stake, + cryptoCurrencyData = cryptoData, + ) + } + }, + onQuickActionLongClick = { + if (it == QuickActionUM.Receive) { + tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.CopyAddress, + cryptoCurrencyData = cryptoData, + ) + } + }, + ) } - }.toImmutableList() + + private fun toQuickActions(actions: List) = buildList { + actions.forEach { action -> + if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { + when (action) { + is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy + is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(showBadge = action.showBadge) + is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive + is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake + else -> null + }?.let(::add) + } + } + }.toImmutableList() + } } \ No newline at end of file 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 0439b74aea..1a9c902d2a 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 @@ -14,10 +14,7 @@ import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.message.DialogMessage -import com.tangem.domain.models.Asset -import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.TokenReceiveConfig -import com.tangem.domain.models.TokenReceiveNotification import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkStatus @@ -26,8 +23,7 @@ import com.tangem.domain.nft.GetNFTCurrencyUseCase import com.tangem.domain.nft.GetNFTNetworkStatusUseCase import com.tangem.domain.nft.GetNFTNetworksUseCase import com.tangem.domain.nft.analytics.NFTAnalyticsEvent -import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase -import com.tangem.domain.transaction.usecase.GetEnsNameUseCase +import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory import com.tangem.features.nft.impl.R import com.tangem.features.nft.receive.NFTReceiveComponent import com.tangem.features.nft.receive.entity.NFTReceiveUM @@ -36,7 +32,6 @@ import com.tangem.features.nft.receive.entity.transformer.ToggleSearchBarTransfo import com.tangem.features.nft.receive.entity.transformer.UpdateDataStateTransformer import com.tangem.features.nft.receive.entity.transformer.UpdateSearchQueryTransformer import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle -import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.* @@ -56,9 +51,8 @@ internal class NFTReceiveModel @Inject constructor( private val analyticsEventHandler: AnalyticsEventHandler, private val messageSender: UiMessageSender, private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle, - private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, - private val getEnsNameUseCase: GetEnsNameUseCase, private val getNFTCurrencyUseCase: GetNFTCurrencyUseCase, + private val receiveAddressesFactory: ReceiveAddressesFactory, paramsContainer: ParamsContainer, ) : Model() { @@ -196,54 +190,11 @@ internal class NFTReceiveModel @Inject constructor( private suspend fun configureReceiveAddresses(addresses: NetworkAddress, network: Network): TokenReceiveConfig { val cryptoCurrency = getNFTCurrencyUseCase.invoke(network) - - val ensName = getEnsNameUseCase.invoke( + return receiveAddressesFactory.createForNft( userWalletId = params.userWalletId, + addresses = addresses, network = network, - address = addresses.defaultAddress.value, - ) - - val receiveAddresses = buildList { - ensName?.let { ens -> - add( - ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Ens, - value = ens, - ), - ) - } - addresses.availableAddresses.map { address -> - add( - ReceiveAddressModel( - nameService = when (address.type) { - NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default - NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy - }, - value = address.value, - ), - ) - } - } - - val notifications = buildList { - if (BlockchainUtils.isSolana(network.rawId)) { - add( - TokenReceiveNotification( - title = R.string.nft_receive_unsupported_types, - subtitle = R.string.nft_receive_unsupported_types_description, - ), - ) - } - } - - return TokenReceiveConfig( - shouldShowWarning = Asset.NFT.name !in getViewedTokenReceiveWarningUseCase(), - cryptoCurrency = cryptoCurrency, - userWalletId = params.userWalletId, - showMemoDisclaimer = false, - receiveAddress = receiveAddresses, - tokenReceiveNotification = notifications, - asset = Asset.NFT, + nft = cryptoCurrency, ) } } \ No newline at end of file diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt index 1bcaa44999..cd04093e59 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt @@ -47,8 +47,8 @@ internal class AccountAwardConverter( id = currency.id.value, iconState = CryptoCurrencyToIconStateConverter().convert(currency), titleState = TokenItemState.TitleState.Content(stringReference(currency.name)), - fiatAmountState = TokenItemState.FiatAmountState.Content(text = ""), - subtitle2State = TokenItemState.Subtitle2State.TextContent(text = ""), + fiatAmountState = null, + subtitle2State = null, subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(currency.symbol)), onItemClick = null, onItemLongClick = null, 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 8397d73477..027e74d291 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 @@ -36,12 +36,9 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase -import com.tangem.domain.models.ReceiveAddressModel -import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.models.TokenReceiveNotification import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus -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 @@ -139,8 +136,7 @@ internal class TokenDetailsModel @Inject constructor( private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener, private val analyticsExceptionHandler: AnalyticsExceptionHandler, private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle, - private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, - private val getEnsNameUseCase: GetEnsNameUseCase, + private val receiveAddressesFactory: ReceiveAddressesFactory, private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles, private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase, private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase, @@ -1058,34 +1054,10 @@ internal class TokenDetailsModel @Inject constructor( .launchIn(modelScope) } - private suspend fun configureReceiveAddresses(addresses: NetworkAddress): TokenDetailsBottomSheetConfig { - val ensName = getEnsNameUseCase.invoke( - userWalletId = userWalletId, - network = cryptoCurrency.network, - address = addresses.defaultAddress.value, - ) - - val receiveAddresses = buildList { - ensName?.let { ens -> - add( - ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Ens, - value = ens, - ), - ) - } - addresses.availableAddresses.map { address -> - add( - ReceiveAddressModel( - nameService = when (address.type) { - NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default - NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy - }, - value = address.value, - ), - ) - } - } + private suspend fun configureReceiveAddresses( + cryptoCurrencyStatus: CryptoCurrencyStatus?, + ): TokenDetailsBottomSheetConfig? { + cryptoCurrencyStatus ?: return null val notifications = buildList { if (isActiveYieldSupply()) { @@ -1099,16 +1071,13 @@ internal class TokenDetailsModel @Inject constructor( } } - return TokenDetailsBottomSheetConfig.Receive( - TokenReceiveConfig( - shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(), - cryptoCurrency = cryptoCurrency, - userWalletId = userWalletId, - showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network.TransactionExtrasType.NONE, - tokenReceiveNotification = notifications, - receiveAddress = receiveAddresses, - ), - ) + val receiveConfig = receiveAddressesFactory.create( + status = cryptoCurrencyStatus, + userWalletId = userWalletId, + notifications = notifications, + ) ?: return null + + return TokenDetailsBottomSheetConfig.Receive(receiveConfig) } private fun sendOneTimeBalanceLoadedAnalyticsEvent(cryptoCurrencyStatus: CryptoCurrencyStatus?) { @@ -1186,9 +1155,8 @@ internal class TokenDetailsModel @Inject constructor( val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) { modelScope.launch { - bottomSheetNavigation.activate( - configuration = configureReceiveAddresses(addresses = networkAddress), - ) + configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus) + ?.let { bottomSheetNavigation.activate(it) } } } else { analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(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 c2c173c0e9..9fd48ed6b2 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 @@ -29,7 +29,6 @@ import com.tangem.domain.core.utils.lceError import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.markets.TokenMarketParams -import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -52,7 +51,7 @@ 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 import com.tangem.domain.tokens.model.details.TokenAction -import com.tangem.domain.transaction.usecase.GetEnsNameUseCase +import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase @@ -145,9 +144,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( private val appRouter: AppRouter, private val rampStateManager: RampStateManager, private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle, - private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase, - private val getEnsNameUseCase: GetEnsNameUseCase, + private val receiveAddressesFactory: ReceiveAddressesFactory, private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles, private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase, private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase, @@ -708,44 +706,10 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( } private suspend fun configureReceiveAddresses(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenReceiveConfig? { - val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return null val userWalletId = stateHolder.getSelectedWalletId() - - val ensName = getEnsNameUseCase.invoke( + return receiveAddressesFactory.create( + status = cryptoCurrencyStatus, userWalletId = userWalletId, - network = cryptoCurrencyStatus.currency.network, - address = networkAddress.defaultAddress.value, - ) - - val receiveAddresses = buildList { - ensName?.let { ens -> - add( - ReceiveAddressModel( - nameService = ReceiveAddressModel.NameService.Ens, - value = ens, - ), - ) - } - networkAddress.availableAddresses.map { address -> - add( - ReceiveAddressModel( - nameService = when (address.type) { - NetworkAddress.Address.Type.Primary -> ReceiveAddressModel.NameService.Default - NetworkAddress.Address.Type.Secondary -> ReceiveAddressModel.NameService.Legacy - }, - value = address.value, - ), - ) - } - } - - return TokenReceiveConfig( - shouldShowWarning = cryptoCurrencyStatus.currency.name !in getViewedTokenReceiveWarningUseCase(), - cryptoCurrency = cryptoCurrencyStatus.currency, - userWalletId = userWalletId, - showMemoDisclaimer = cryptoCurrencyStatus.currency.network.transactionExtrasType != Network - .TransactionExtrasType.NONE, - receiveAddress = receiveAddresses, ) }