Updated on 2026-08-14
This commit is contained in:
parent
4032360f13
commit
4dc6dffd67
16 changed files with 381 additions and 50 deletions
|
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.serialization)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
|
|
|
|||
|
|
@ -15,15 +15,16 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.tokenreceive.TokenReceiveComponent
|
||||
import com.tangem.features.txhistory.component.TxHistoryComponent
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -35,6 +36,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory,
|
||||
txHistoryComponentFactory: TxHistoryComponent.Factory,
|
||||
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
|
||||
private val yieldSupplyWarningComponentFactory: YieldSupplyDepositedWarningComponent.Factory,
|
||||
yieldSupplyComponentFactory: YieldSupplyComponent.Factory,
|
||||
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
|
|
@ -50,7 +52,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
source = model.bottomSheetNavigation,
|
||||
serializer = TokenReceiveConfig.serializer(),
|
||||
serializer = TokenDetailsBottomSheetConfig.serializer(),
|
||||
handleBackButton = false,
|
||||
childFactory = ::bottomSheetChild,
|
||||
)
|
||||
|
|
@ -98,15 +100,26 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun bottomSheetChild(
|
||||
config: TokenReceiveConfig,
|
||||
route: TokenDetailsBottomSheetConfig,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = tokenReceiveComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = config,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
): ComposableBottomSheetComponent = when (route) {
|
||||
is TokenDetailsBottomSheetConfig.Receive -> tokenReceiveComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = TokenReceiveComponent.Params(
|
||||
config = route.tokenReceiveConfig,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
is TokenDetailsBottomSheetConfig.YieldSupplyWarning -> yieldSupplyWarningComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = YieldSupplyDepositedWarningComponent.Params(
|
||||
cryptoCurrency = route.cryptoCurrency,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
modelCallback = model,
|
||||
tokenAction = route.tokenAction,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : TokenDetailsComponent.Factory {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import arrow.core.getOrElse
|
|||
import arrow.core.merge
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
|
|
@ -58,6 +59,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
|
|||
import com.tangem.domain.tokens.model.analytics.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.DetailsScreenOpened.TokenBalance
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import com.tangem.domain.transaction.error.AssociateAssetError
|
||||
import com.tangem.domain.transaction.error.IncompleteTransactionError
|
||||
import com.tangem.domain.transaction.error.OpenTrustlineError
|
||||
|
|
@ -72,6 +74,7 @@ import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListen
|
|||
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsCurrencyStatusAnalyticsSender
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
|
|
@ -81,6 +84,7 @@ import com.tangem.features.tokendetails.TokenDetailsComponent
|
|||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
|
||||
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
|
|
@ -139,7 +143,10 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase,
|
||||
private val getEnsNameUseCase: GetEnsNameUseCase,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
) : Model(), TokenDetailsClickIntents {
|
||||
private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase,
|
||||
private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase,
|
||||
private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase,
|
||||
) : Model(), TokenDetailsClickIntents, YieldSupplyDepositedWarningComponent.ModelCallback {
|
||||
|
||||
private val params = paramsContainer.require<TokenDetailsComponent.Params>()
|
||||
private val userWalletId: UserWalletId = params.userWalletId
|
||||
|
|
@ -162,7 +169,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
/** Transaction id to check for status */
|
||||
private val waitForFirstExpressStatusEmmit = MutableStateFlow(false)
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<TokenReceiveConfig> = SlotNavigation()
|
||||
val bottomSheetNavigation: SlotNavigation<TokenDetailsBottomSheetConfig> = SlotNavigation()
|
||||
|
||||
private val stateFactory = TokenDetailsStateFactory(
|
||||
currentStateProvider = Provider { uiState.value },
|
||||
|
|
@ -527,8 +534,18 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
sendCurrency()
|
||||
modelScope.launch {
|
||||
if (needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)) {
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
tokenAction = TokenAction.Send,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
sendCurrency()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCurrency() {
|
||||
|
|
@ -563,26 +580,15 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
modelScope.launch {
|
||||
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
|
||||
if (needShowYieldSupplyWarning()) {
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = configureReceiveAddresses(addresses = networkAddress),
|
||||
configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
tokenAction = TokenAction.Receive,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
|
||||
internalUiState.value = stateFactory.getStateWithReceiveBottomSheet(
|
||||
currency = cryptoCurrency,
|
||||
networkAddress = networkAddress,
|
||||
onCopyClick = {
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol))
|
||||
clipboardManager.setText(text = it, isSensitive = true)
|
||||
},
|
||||
onShareClick = {
|
||||
analyticsEventsHandler.send(
|
||||
TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol),
|
||||
)
|
||||
shareManager.shareText(text = it)
|
||||
},
|
||||
)
|
||||
navigateToReceive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -656,13 +662,24 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
screenSource = AnalyticsParam.ScreensSources.Token.value,
|
||||
),
|
||||
)
|
||||
modelScope.launch {
|
||||
if (needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)) {
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
tokenAction = TokenAction.Swap,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
appRouter.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
screenSource = AnalyticsParam.ScreensSources.Token.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDismissDialog() {
|
||||
|
|
@ -1040,7 +1057,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private suspend fun configureReceiveAddresses(addresses: NetworkAddress): TokenReceiveConfig {
|
||||
private suspend fun configureReceiveAddresses(addresses: NetworkAddress): TokenDetailsBottomSheetConfig {
|
||||
val ensName = getEnsNameUseCase.invoke(
|
||||
userWalletId = userWalletId,
|
||||
network = cryptoCurrency.network,
|
||||
|
|
@ -1069,12 +1086,14 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
return TokenReceiveConfig(
|
||||
shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(),
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
|
||||
receiveAddress = receiveAddresses,
|
||||
return TokenDetailsBottomSheetConfig.Receive(
|
||||
TokenReceiveConfig(
|
||||
shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(),
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
|
||||
receiveAddress = receiveAddresses,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1115,6 +1134,59 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
isBalanceLoadedEventSent = true
|
||||
}
|
||||
|
||||
private suspend fun needShowYieldSupplyWarning(): Boolean {
|
||||
return yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled &&
|
||||
needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)
|
||||
}
|
||||
|
||||
override fun onYieldSupplyWarningAcknowledged(tokenAction: TokenAction) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
modelScope.launch {
|
||||
saveViewedYieldSupplyWarningUseCase(cryptoCurrency.name)
|
||||
if (tokenAction == TokenAction.Receive) {
|
||||
saveViewedTokenReceiveWarningUseCase(cryptoCurrency.name)
|
||||
}
|
||||
when (tokenAction) {
|
||||
TokenAction.Receive -> navigateToReceive()
|
||||
TokenAction.Send -> sendCurrency()
|
||||
TokenAction.Swap -> appRouter.push(
|
||||
AppRoute.Swap(
|
||||
currencyFrom = cryptoCurrency,
|
||||
userWalletId = userWalletId,
|
||||
screenSource = AnalyticsParam.ScreensSources.Token.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToReceive() {
|
||||
val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return
|
||||
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
|
||||
modelScope.launch {
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = configureReceiveAddresses(addresses = networkAddress),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
|
||||
internalUiState.value = stateFactory.getStateWithReceiveBottomSheet(
|
||||
currency = cryptoCurrency,
|
||||
networkAddress = networkAddress,
|
||||
onCopyClick = {
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol))
|
||||
clipboardManager.setText(text = it, isSensitive = true)
|
||||
},
|
||||
onShareClick = {
|
||||
analyticsEventsHandler.send(
|
||||
TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol),
|
||||
)
|
||||
shareManager.shareText(text = it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.route
|
||||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.details.TokenAction
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class TokenDetailsBottomSheetConfig : Route {
|
||||
|
||||
@Serializable
|
||||
data class Receive(val tokenReceiveConfig: TokenReceiveConfig) : TokenDetailsBottomSheetConfig()
|
||||
|
||||
@Serializable
|
||||
data class YieldSupplyWarning(
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val tokenAction: TokenAction,
|
||||
) : TokenDetailsBottomSheetConfig()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue