Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-21 17:11:54 +02:00
parent 33fd5a927a
commit b8f3d5315f
20 changed files with 607 additions and 118 deletions

View file

@ -23,11 +23,13 @@ import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.markets.entry.MarketsEntryComponent
import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent
import com.tangem.features.pushnotifications.api.PushNotificationsParams
import com.tangem.features.tokenreceive.TokenReceiveComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
internal class WalletComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted navigate: (WalletRoute) -> Unit,
@ -35,6 +37,7 @@ internal class WalletComponent @AssistedInject constructor(
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
private val askBiometryComponentFactory: AskBiometryComponent.Factory,
private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val model: WalletModel = getOrCreateModel()
@ -76,6 +79,15 @@ internal class WalletComponent @AssistedInject constructor(
modelCallbacks = model.askForPushNotificationsModelCallbacks,
),
)
is WalletDialogConfig.TokenReceive -> {
tokenReceiveComponentFactory.create(
context = childByContext(componentContext),
params = TokenReceiveComponent.Params(
config = dialogConfig.tokenReceiveConfig,
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
),
)
}
}
},
)

View file

@ -29,6 +29,8 @@ 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
import com.tangem.domain.models.network.Network
@ -41,6 +43,7 @@ import com.tangem.domain.promo.models.StoryContentIds
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.GetViewedTokenReceiveWarningUseCase
import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
import com.tangem.domain.tokens.RemoveCurrencyUseCase
import com.tangem.domain.tokens.legacy.TradeCryptoAction
@ -49,6 +52,7 @@ import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
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.transaction.usecase.GetEnsNameUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
@ -61,6 +65,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
@ -128,6 +133,9 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val shareManager: ShareManager,
private val appRouter: AppRouter,
private val rampStateManager: RampStateManager,
private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
private val getEnsNameUseCase: GetEnsNameUseCase,
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
override fun onSendClick(
@ -172,13 +180,22 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
event?.let { analyticsEventHandler.send(it) }
stateHolder.showBottomSheet(
createReceiveBottomSheetContent(
currency = cryptoCurrencyStatus.currency,
addresses = cryptoCurrencyStatus.value.networkAddress ?: return,
),
userWalletId,
)
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
stateHolder.hideBottomSheet()
modelScope.launch {
configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)?.let {
router.openTokenReceiveBottomSheet(it)
}
}
} else {
stateHolder.showBottomSheet(
createReceiveBottomSheetContent(
currency = cryptoCurrencyStatus.currency,
addresses = cryptoCurrencyStatus.value.networkAddress ?: return,
),
userWalletId,
)
}
}
override fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference? {
@ -639,4 +656,45 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
targetRoute
}
}
private suspend fun configureReceiveAddresses(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenReceiveConfig? {
val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return null
val userWalletId = stateHolder.getSelectedWalletId()
val ensName = getEnsNameUseCase.invoke(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
address = networkAddress.defaultAddress.value,
)
val receiveAddresses = buildList {
ensName?.let { ens ->
add(
ReceiveAddressModel(
nameService = ReceiveAddressModel.NameService.Ens,
value = ens,
displayName = ens,
),
)
}
networkAddress.availableAddresses.map { address ->
add(
ReceiveAddressModel(
nameService = ReceiveAddressModel.NameService.Default,
value = address.value,
displayName = "${cryptoCurrencyStatus.currency.name} (${cryptoCurrencyStatus.currency.symbol})",
),
)
}
}
return TokenReceiveConfig(
shouldShowWarning = cryptoCurrencyStatus.currency.name !in getViewedTokenReceiveWarningUseCase(),
cryptoCurrency = cryptoCurrencyStatus.currency,
userWalletId = userWalletId,
showMemoDisclaimer = cryptoCurrencyStatus.currency.network.transactionExtrasType != Network
.TransactionExtrasType.NONE,
receiveAddress = receiveAddresses,
)
}
}

View file

@ -1,11 +1,13 @@
package com.tangem.feature.wallet.presentation.router
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRoute.ManageTokens.Source
import com.tangem.common.routing.AppRouter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
@ -98,4 +100,10 @@ internal class DefaultWalletRouter @Inject constructor(
),
)
}
override fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig) {
dialogNavigation.activate(
configuration = WalletDialogConfig.TokenReceive(tokenReceiveConfig),
)
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.router
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
@ -54,4 +55,6 @@ internal interface InnerWalletRouter {
/** Open NFT collections screen */
fun openNFT(userWallet: UserWallet)
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
}

View file

@ -90,6 +90,16 @@ internal class WalletStateController @Inject constructor() {
)
}
fun hideBottomSheet() {
update(
transformer = OpenBottomSheetTransformer(
userWalletId = getSelectedWalletId(),
content = TangemBottomSheetConfigContent.Empty,
onDismissBottomSheet = {},
),
)
}
private fun getInitialState(): WalletScreenState {
return WalletScreenState(
topBarConfig = WalletTopBarConfig(onDetailsClick = {}),

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
@ -19,4 +20,7 @@ internal sealed interface WalletDialogConfig {
@Serializable
data object AskForPushNotifications : WalletDialogConfig
@Serializable
data class TokenReceive(val tokenReceiveConfig: TokenReceiveConfig) : WalletDialogConfig
}