Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-01 15:44:36 +05:00
parent a1a82d3ece
commit b27ff31652
52 changed files with 860 additions and 518 deletions

View file

@ -9,7 +9,6 @@ import com.tangem.feature.stories.api.StoriesComponent
import com.tangem.feature.usedesk.api.UsedeskComponent
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.features.account.AccountCreateEditComponent
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.account.AccountDetailsComponent
import com.tangem.features.account.ArchivedAccountListComponent
import com.tangem.features.createwalletselection.CreateWalletSelectionComponent
@ -116,7 +115,6 @@ internal class ChildFactory @Inject constructor(
private val surveyComponentFactory: SurveyComponent.Factory,
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
) {
@Suppress("LongMethod", "CyclomaticComplexMethod")
@ -237,13 +235,6 @@ internal class ChildFactory @Inject constructor(
componentFactory = buyCryptoComponentFactory,
)
}
is AppRoute.AddFunds -> {
createComponentChild(
context = context,
params = AddFundsComponent.Params(userWalletId = route.userWalletId),
componentFactory = addFundsComponentFactory,
)
}
is AppRoute.SellCrypto -> {
createComponentChild(
context = context,

View file

@ -59,11 +59,6 @@ sealed class AppRoute(val path: String) : Route {
@Serializable
data object Wallet : AppRoute(path = "/wallet")
@Serializable
data class AddFunds(
val userWalletId: UserWalletId,
) : AppRoute(path = "/add_funds/${userWalletId.stringValue}")
@Serializable
data class CurrencyDetails(
val userWalletId: UserWalletId,

View file

@ -34,7 +34,7 @@ class TokenActionsHandler @AssistedInject constructor(
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
@Assisted private val currentAppCurrency: Provider<AppCurrency>,
@Assisted private val onHandleQuickAction: (HandledQuickAction) -> Unit,
@Assisted private val onHandleQuickAction: (action: HandledQuickAction, shouldDismiss: Boolean) -> Unit,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val messageSender: UiMessageSender,
) {
@ -49,6 +49,18 @@ class TokenActionsHandler @AssistedInject constructor(
action = action,
cryptoCurrencyData = cryptoCurrencyData,
),
when (action) {
TokenActionsBSContentUM.Action.Receive,
TokenActionsBSContentUM.Action.CopyAddress,
TokenActionsBSContentUM.Action.Sell,
-> false
TokenActionsBSContentUM.Action.Send,
TokenActionsBSContentUM.Action.Stake,
TokenActionsBSContentUM.Action.YieldMode,
TokenActionsBSContentUM.Action.Buy,
TokenActionsBSContentUM.Action.Exchange,
-> true
},
)
val userWallet = cryptoCurrencyData.userWallet
if (userWallet is UserWallet.Cold && handleDemoMode(action, userWallet)) return
@ -164,7 +176,7 @@ class TokenActionsHandler @AssistedInject constructor(
interface Factory {
fun create(
currentAppCurrency: Provider<AppCurrency>,
onHandleQuickAction: (HandledQuickAction) -> Unit,
onHandleQuickAction: (HandledQuickAction, shouldDismiss: Boolean) -> Unit,
): TokenActionsHandler
}

View file

@ -1,14 +1,29 @@
package com.tangem.features.commonfeatures.api.addfunds
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
interface AddFundsComponent : ComposableContentComponent {
interface AddFundsComponent : ComposableBottomSheetComponent {
data class Params(
val userWalletId: UserWalletId,
val launchMode: LaunchMode,
val onDismiss: () -> Unit,
)
sealed interface LaunchMode {
data class ChooseToken(val userWalletId: UserWalletId) : LaunchMode
data class TokenActionsOnly(
val userWalletId: UserWalletId,
val currency: CryptoCurrency,
) : LaunchMode
data class FilteredByRawId(
val rawCurrencyId: CryptoCurrency.RawID,
) : LaunchMode
}
interface Factory : ComponentFactory<Params, AddFundsComponent>
}

View file

@ -8,6 +8,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.SharedFlow
@ -94,7 +95,14 @@ interface AddToPortfolioManager : AddToPortfolioManagerInternal {
val wallet: UserWallet,
val account: AccountStatus.CryptoPortfolio,
val addedCurrency: CryptoCurrencyStatus,
val meta: FinishMeta = FinishMeta.None,
)
sealed interface FinishMeta {
data object None : FinishMeta
data object OnQuickAction : FinishMeta
data class OnBottomAction(val action: BottomAction) : FinishMeta
}
}
/**

View file

@ -29,6 +29,7 @@ interface ChooseTokenBridge : ChooseTokenBridgeInternal {
val title: TextReference,
val isShowMarketBlock: Boolean,
val isShowPaymentAccount: Boolean,
val isAppBarShown: Boolean = true,
) {
companion object {
val SwapFrom = Settings(
@ -45,6 +46,7 @@ interface ChooseTokenBridge : ChooseTokenBridgeInternal {
title = resourceReference(R.string.swapping_to_title),
isShowMarketBlock = true,
isShowPaymentAccount = false,
isAppBarShown = false,
)
}
}
@ -90,6 +92,11 @@ data class ChooseTokenResult(
val analyticsPayload: Set<ChooseTokenAnalyticsPayload> = emptySet(),
) {
val walletId get() = wallet.walletId
val wasJustAdded: Boolean
get() = analyticsPayload
.filterIsInstance<ChooseTokenAnalyticsPayload.IsMarketTokenSelected>()
.any { it.value }
}
sealed interface ChooseTokenAnalyticsPayload {

View file

@ -0,0 +1,3 @@
package com.tangem.features.commonfeatures.api.tokenactions
enum class BottomAction { GoToToken, None }

View file

@ -1,93 +1,232 @@
package com.tangem.features.commonfeatures.impl.addfunds
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.LocalTangemBottomSheetContentBottomInset
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemeRedesign
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.addfunds.model.uiSpec
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import com.tangem.core.ui.R as CoreR
@Suppress("LongParameterList")
internal class DefaultAddFundsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: AddFundsComponent.Params,
chooseTokenComponentFactory: ChooseTokenComponent.Factory,
tokenActionsComponentFactory: TokenActionsComponent.Factory,
userPortfolioComponentFactory: UserPortfolioComponent.Factory,
walletFeatureToggles: WalletFeatureToggles,
) : AppComponentContext by appComponentContext, AddFundsComponent {
private val model: AddFundsModel = getOrCreateModel(params)
private val chooseTokenComponent: ChooseTokenComponent = chooseTokenComponentFactory.create(
context = child(key = "addFundsChooseToken"),
params = ChooseTokenComponent.Params(bridge = model.chooseTokenBridge),
)
private val isCompactTokenActions: Boolean = params.launchMode is AddFundsComponent.LaunchMode.TokenActionsOnly
private val tokenActionsComponent: TokenActionsComponent = tokenActionsComponentFactory.create(
context = child(key = "addFundsTokenActions"),
params = TokenActionsComponent.Params(
data = model.tokenActionsData,
callbacks = model,
bottomAction = TokenActionsComponent.BottomAction.GoToToken,
isRedesignForced = true,
),
)
private val isAddFundsStage1Enabled: Boolean = walletFeatureToggles.isAddFundsStage1Enabled
private val tokenActionsComponent: TokenActionsComponent by lazy {
tokenActionsComponentFactory.create(
context = child(key = "addFundsTokenActions"),
params = TokenActionsComponent.Params(
data = model.tokenActionsData,
callbacks = model,
bottomAction = model.currentBottomAction,
isRedesignForced = true,
isCompact = isCompactTokenActions,
),
)
}
private val chooseTokenComponent: ChooseTokenComponent? by lazy {
(params.launchMode as? AddFundsComponent.LaunchMode.ChooseToken)?.let {
chooseTokenComponentFactory.create(
context = child(key = "addFundsChooseToken"),
params = ChooseTokenComponent.Params(
bridge = model.chooseTokenBridge,
),
)
}
}
private val userPortfolioComponent: UserPortfolioComponent by lazy {
userPortfolioComponentFactory.create(
context = child(key = "addFundsUserPortfolio"),
params = UserPortfolioComponent.Params(
uiState = model.userPortfolioStateController.uiState,
callbacks = object : UserPortfolioComponent.Callbacks {
override fun onContinueFromUserPortfolio() = Unit
},
),
)
}
override fun dismiss() = model.onDismiss()
@Composable
override fun Content(modifier: Modifier) {
chooseTokenComponent.Content(modifier)
val isTokenActionsShown by model.isTokenActionsShown.collectAsStateWithLifecycle()
if (isTokenActionsShown) {
// force use redesign theme here according to the task requirements, will be reworked in the next release
TangemThemeRedesign {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = model::onTokenActionsDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors2.surface.level2,
scrollableContent = true,
title = {
TangemModalBottomSheetTitle(
modifier = Modifier.fillMaxWidth(),
title = resourceReference(R.string.common_get_token),
endIconRes = R.drawable.ic_close_24,
onEndClick = model::onTokenActionsDismiss,
)
},
content = { _ ->
Column(
modifier = Modifier.padding(
start = TangemTheme.dimens2.x4,
top = TangemTheme.dimens2.x2,
end = TangemTheme.dimens2.x4,
bottom = TangemTheme.dimens2.x4,
),
) {
tokenActionsComponent.Content(Modifier)
}
},
)
}
override fun BottomSheet() {
val route by model.uiRoute.collectAsStateWithLifecycle()
val canGoBack by model.canGoBack.collectAsStateWithLifecycle()
LaunchedEffect(route) {
if (route != AddFundsModel.UiRoute.UserPortfolio) return@LaunchedEffect
val mode = params.launchMode as? AddFundsComponent.LaunchMode.FilteredByRawId ?: return@LaunchedEffect
model.userPortfolioStateController.updateAndWaitNotNullState(
allAvailableData = model.buildAvailableToAddDataForChooser(),
rawCurrencyId = mode.rawCurrencyId,
)
}
WithOptionalRedesignTheme(isEnabled = isAddFundsStage1Enabled) {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
onBack = if (canGoBack) model::onBack else ::dismiss,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
),
type = when (params.launchMode) {
is AddFundsComponent.LaunchMode.TokenActionsOnly -> TangemBottomSheetType.Modal
is AddFundsComponent.LaunchMode.ChooseToken -> TangemBottomSheetType.Default
is AddFundsComponent.LaunchMode.FilteredByRawId ->
if (route is AddFundsModel.UiRoute.TokenActions) {
TangemBottomSheetType.Default
} else {
TangemBottomSheetType.Modal
}
},
containerColor = TangemTheme.colors2.surface.level2,
title = {
AddFundsBottomSheetTitle(
route = route,
canGoBack = canGoBack,
onBackClick = model::onBack,
onCloseClick = ::dismiss,
)
},
content = {
val animatedContentModifier =
if (params.launchMode is AddFundsComponent.LaunchMode.ChooseToken) {
Modifier.fillMaxSize()
} else {
Modifier
}
AnimatedContent(
targetState = route,
modifier = animatedContentModifier,
label = "AddFundsContentAnimation",
) { animatedRoute ->
AddFundsRouteContent(
route = animatedRoute,
shouldFillHeight = !isCompactTokenActions && animatedRoute.uiSpec().shouldFillHeight,
)
}
},
)
}
}
@Composable
private fun AddFundsRouteContent(route: AddFundsModel.UiRoute, shouldFillHeight: Boolean) {
val spec = route.uiSpec()
val horizontalPadding = if (spec.shouldApplyHorizontalPadding) {
Modifier.padding(horizontal = TangemTheme.dimens2.x4)
} else {
Modifier
}
val sizeModifier = if (shouldFillHeight) Modifier.fillMaxSize() else Modifier.fillMaxWidth()
RenderRoute(route, horizontalPadding.then(sizeModifier))
}
@Composable
private fun RenderRoute(route: AddFundsModel.UiRoute, modifier: Modifier = Modifier) {
when (route) {
AddFundsModel.UiRoute.Loading -> Unit
AddFundsModel.UiRoute.ChooseToken -> chooseTokenComponent?.Content(modifier)
AddFundsModel.UiRoute.UserPortfolio -> CompositionLocalProvider(
LocalTangemBottomSheetContentBottomInset provides TangemTheme.dimens2.x4,
) {
userPortfolioComponent.Content(modifier)
}
AddFundsModel.UiRoute.TokenActions -> tokenActionsComponent.Content(modifier)
}
}
@Composable
private fun AddFundsBottomSheetTitle(
route: AddFundsModel.UiRoute,
canGoBack: Boolean,
onBackClick: () -> Unit,
onCloseClick: () -> Unit,
) {
TangemTopBar(
title = route.uiSpec().title,
type = TangemTopBarType.BottomSheet,
startContent = if (canGoBack) {
{ CircleIconButton(iconRes = CoreR.drawable.ic_arrow_back_28, onClick = onBackClick) }
} else {
null
},
endContent = {
CircleIconButton(iconRes = R.drawable.ic_close_24, onClick = onCloseClick)
},
)
}
@Composable
private fun WithOptionalRedesignTheme(isEnabled: Boolean, content: @Composable () -> Unit) {
if (isEnabled) {
TangemThemeRedesign(content = content)
} else {
content()
}
}
@Composable
private fun CircleIconButton(iconRes: Int, onClick: () -> Unit) {
Icon(
imageVector = ImageVector.vectorResource(id = iconRes),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier
.size(TangemTheme.dimens2.x11)
.background(
color = TangemTheme.colors2.button.backgroundSecondary,
shape = CircleShape,
)
.clickableSingle(onClick = onClick)
.padding(TangemTheme.dimens2.x2),
)
}
@AssistedFactory

View file

@ -19,8 +19,6 @@ internal sealed class AddFundsAnalyticsEvent(
class ButtonReceive : AddFundsAnalyticsEvent(event = "Button - Receive")
class ButtonGoToToken : AddFundsAnalyticsEvent(event = "Button - Go to Token")
companion object {
private const val CATEGORY = "Add Funds"
const val SOURCE_MAIN_SCREEN = "Main Screen"

View file

@ -1,5 +1,6 @@
package com.tangem.features.commonfeatures.impl.addfunds.model
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.markets.action.CryptoCurrencyData
@ -8,14 +9,25 @@ 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.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddData
import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddWallet
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.commonfeatures.impl.addfunds.analytics.AddFundsAnalyticsEvent
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.userportfolio.state.UserPortfolioStateController
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
@ -23,98 +35,257 @@ import kotlinx.coroutines.launch
import javax.inject.Inject
@ModelScoped
@Suppress("LongParameterList")
internal class AddFundsModel @Inject constructor(
paramsContainer: ParamsContainer,
chooseTokenBridgeFactory: ChooseTokenBridge.Factory,
userPortfolioStateControllerFactory: UserPortfolioStateController.Factory,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
private val appRouter: AppRouter,
private val multiAccountStatusListSupplier: MultiAccountStatusListSupplier,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val appRouter: AppRouter,
override val dispatchers: CoroutineDispatcherProvider,
) : Model(), TokenActionsComponent.Callbacks {
private val params = paramsContainer.require<AddFundsComponent.Params>()
val launchMode: AddFundsComponent.LaunchMode = params.launchMode
val chooseTokenBridge: ChooseTokenBridge = chooseTokenBridgeFactory.create(
modelScope = modelScope,
settings = ChooseTokenBridge.Settings.AddFunds,
analyticsPayload = setOf(
ChooseTokenAnalyticsPayload.ScreensSources(SCREEN_SOURCE),
),
)
private val routeStack = MutableStateFlow(listOf<UiRoute>(UiRoute.Loading))
private val selectedToken = MutableStateFlow<ChooseTokenResult?>(null)
val uiRoute: StateFlow<UiRoute> = routeStack
.map { it.last() }
.distinctUntilChanged()
.stateIn(modelScope, SharingStarted.Eagerly, initialValue = UiRoute.Loading)
val isTokenActionsShown: StateFlow<Boolean> = selectedToken
.map { it != null }
val canGoBack: StateFlow<Boolean> = routeStack
.map { it.size > 1 }
.distinctUntilChanged()
.stateIn(modelScope, SharingStarted.Eagerly, initialValue = false)
private val tokenActionsTrigger = MutableStateFlow<TokenActionsRequest?>(null)
private val filteredEntries = MutableStateFlow<List<FilteredEntry>>(emptyList())
val currentBottomAction: MutableStateFlow<BottomAction> =
MutableStateFlow(BottomAction.None)
@OptIn(ExperimentalCoroutinesApi::class)
val tokenActionsData: Flow<CryptoCurrencyData> = selectedToken
val tokenActionsData: Flow<CryptoCurrencyData> = tokenActionsTrigger
.filterNotNull()
.flatMapLatest { result ->
val cryptoPortfolio = result.account as? AccountStatus.CryptoPortfolio
?: return@flatMapLatest emptyFlow()
getCryptoCurrencyActionsUseCase(
accountId = cryptoPortfolio.account.accountId,
currency = result.currency.currency,
).map { actionsState ->
.flatMapLatest { request ->
combine(
getCryptoCurrencyActionsUseCase(
accountId = request.account.account.accountId,
currency = request.status.currency,
),
isAccountsModeEnabledUseCase(),
) { actionsState, isAccountMode ->
CryptoCurrencyData(
userWallet = result.wallet,
status = result.currency,
userWallet = request.userWallet,
status = request.status,
actions = actionsState.states,
isAccountMode = false,
account = cryptoPortfolio,
isAccountMode = isAccountMode,
account = request.account,
)
}
}
val chooseTokenBridge: ChooseTokenBridge by lazy {
chooseTokenBridgeFactory.create(
modelScope = modelScope,
settings = ChooseTokenBridge.Settings.AddFunds,
analyticsPayload = setOf(ChooseTokenAnalyticsPayload.ScreensSources(SCREEN_SOURCE)),
)
}
val userPortfolioStateController: UserPortfolioStateController = userPortfolioStateControllerFactory.create(
modelScope = modelScope,
onTokenSelected = { result ->
openTokenActions(
request = TokenActionsRequest(
userWallet = result.wallet,
account = result.account,
status = result.addedCurrency,
),
bottomAction = BottomAction.None,
)
},
)
init {
chooseTokenBridge.selectWalletTab(params.userWalletId)
analyticsEventHandler.send(
AddFundsAnalyticsEvent.MethodScreenOpened(source = AddFundsAnalyticsEvent.SOURCE_MAIN_SCREEN),
)
observeBridge()
when (val mode = launchMode) {
is AddFundsComponent.LaunchMode.ChooseToken -> initChooseToken(mode)
is AddFundsComponent.LaunchMode.TokenActionsOnly -> initTokenActionsOnly(mode)
is AddFundsComponent.LaunchMode.FilteredByRawId -> initFilteredByRawId(mode)
}
}
override fun onBottomActionClick() {
val result = selectedToken.value ?: return
selectedToken.value = null
analyticsEventHandler.send(AddFundsAnalyticsEvent.ButtonGoToToken())
appRouter.replaceCurrent(
AppRoute.CurrencyDetails(
userWalletId = result.wallet.walletId,
currency = result.currency.currency,
),
)
fun onBack() {
routeStack.update { stack -> if (stack.size > 1) stack.dropLast(1) else stack }
}
override fun onQuickActionClick(action: TokenActionsBSContentUM.Action) {
fun onDismiss() = params.onDismiss()
override fun onBottomActionClick(bottomAction: BottomAction) {
val request = tokenActionsTrigger.value
if (bottomAction == BottomAction.GoToToken && request != null) {
appRouter.push(
AppRoute.CurrencyDetails(
userWalletId = request.userWallet.walletId,
currency = request.status.currency,
),
)
}
params.onDismiss()
}
override fun onQuickActionClick(action: TokenActionsBSContentUM.Action, shouldDismiss: Boolean) {
val event = when (action) {
TokenActionsBSContentUM.Action.Buy -> AddFundsAnalyticsEvent.ButtonBuy()
TokenActionsBSContentUM.Action.Exchange -> AddFundsAnalyticsEvent.ButtonSwap()
TokenActionsBSContentUM.Action.Receive -> AddFundsAnalyticsEvent.ButtonReceive()
else -> return
else -> null
}
event?.let { analyticsEventHandler.send(it) }
if (shouldDismiss) {
params.onDismiss()
}
analyticsEventHandler.send(event)
}
fun onTokenActionsDismiss() {
selectedToken.value = null
fun buildAvailableToAddDataForChooser(): AvailableToAddData {
val byWallet = filteredEntries.value.groupBy { it.userWallet.walletId }
return AvailableToAddData(
availableToAddWallets = byWallet.mapValues { (_, entries) ->
AvailableToAddWallet(
userWallet = entries.first().userWallet,
accounts = entries.map { it.account }.distinct(),
availableNetworks = emptySet(),
availableToAddAccounts = emptyMap(),
)
},
)
}
private fun observeBridge() {
private fun initChooseToken(mode: AddFundsComponent.LaunchMode.ChooseToken) {
chooseTokenBridge.selectWalletTab(mode.userWalletId)
analyticsEventHandler.send(
AddFundsAnalyticsEvent.MethodScreenOpened(source = AddFundsAnalyticsEvent.SOURCE_MAIN_SCREEN),
)
replaceRoot(UiRoute.ChooseToken)
modelScope.launch {
chooseTokenBridge.onCurrencyChosen.receiveAsFlow().collect { result ->
selectedToken.value = result
}
chooseTokenBridge.onCurrencyChosen.receiveAsFlow().collect(::openTokenActionsFromBridge)
}
modelScope.launch {
chooseTokenBridge.onClose.receiveAsFlow().collect {
appRouter.pop()
chooseTokenBridge.onClose.receiveAsFlow().collect { params.onDismiss() }
}
}
private fun initTokenActionsOnly(mode: AddFundsComponent.LaunchMode.TokenActionsOnly) {
modelScope.launch {
val wallet = getUserWalletUseCase.invokeFlow(mode.userWalletId)
.mapNotNull { it.getOrNull() }
.first()
val match = multiAccountStatusListSupplier()
.first()
.firstOrNull { it.userWalletId == mode.userWalletId }
?.accountStatuses
?.filterCryptoPortfolio()
?.firstNotNullOfOrNull { accountStatus ->
accountStatus.tokenList.flattenCurrencies()
.firstOrNull { it.currency.id == mode.currency.id }
?.let { accountStatus to it }
}
?: run {
params.onDismiss()
return@launch
}
tokenActionsTrigger.value = TokenActionsRequest(wallet, match.first, match.second)
replaceRoot(UiRoute.TokenActions)
}
}
private fun initFilteredByRawId(mode: AddFundsComponent.LaunchMode.FilteredByRawId) {
modelScope.launch {
val entries = collectFilteredEntries(mode.rawCurrencyId)
when (entries.size) {
0 -> params.onDismiss()
1 -> {
val entry = entries.first()
tokenActionsTrigger.value = TokenActionsRequest(entry.userWallet, entry.account, entry.status)
replaceRoot(UiRoute.TokenActions)
}
else -> {
filteredEntries.value = entries
replaceRoot(UiRoute.UserPortfolio)
}
}
}
}
private suspend fun collectFilteredEntries(rawCurrencyId: CryptoCurrency.RawID): List<FilteredEntry> {
val accountLists = multiAccountStatusListSupplier().first()
return accountLists.flatMap { accountStatusList ->
val wallet = getUserWalletUseCase.invokeFlow(accountStatusList.userWalletId)
.mapNotNull { it.getOrNull() }
.firstOrNull()
?: return@flatMap emptyList()
accountStatusList.accountStatuses.filterCryptoPortfolio().flatMap { accountStatus ->
accountStatus.tokenList.flattenCurrencies()
.filter { status ->
val id = status.currency.id.rawCurrencyId ?: return@filter false
getTokenIdIfL2Network(id.value) == rawCurrencyId.value
}
.map { status -> FilteredEntry(wallet, accountStatus, status) }
}
}
}
private fun openTokenActionsFromBridge(result: ChooseTokenResult) {
val account = result.account as? AccountStatus.CryptoPortfolio ?: return
openTokenActions(
request = TokenActionsRequest(result.wallet, account, result.currency),
bottomAction = if (result.wasJustAdded) {
BottomAction.GoToToken
} else {
BottomAction.None
},
)
}
private fun openTokenActions(request: TokenActionsRequest, bottomAction: BottomAction) {
tokenActionsTrigger.value = request
currentBottomAction.value = bottomAction
pushRoute(UiRoute.TokenActions)
}
private fun replaceRoot(route: UiRoute) {
routeStack.value = listOf(route)
}
private fun pushRoute(route: UiRoute) {
routeStack.update { it + route }
}
sealed interface UiRoute {
data object Loading : UiRoute
data object ChooseToken : UiRoute
data object UserPortfolio : UiRoute
data object TokenActions : UiRoute
}
private data class TokenActionsRequest(
val userWallet: UserWallet,
val account: AccountStatus.CryptoPortfolio,
val status: CryptoCurrencyStatus,
)
private data class FilteredEntry(
val userWallet: UserWallet,
val account: AccountStatus.CryptoPortfolio,
val status: CryptoCurrencyStatus,
)
private companion object {
const val SCREEN_SOURCE = "AddFunds"
}

View file

@ -0,0 +1,35 @@
package com.tangem.features.commonfeatures.impl.addfunds.model
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.commonfeatures.impl.R
import com.tangem.core.ui.R as CoreR
internal data class AddFundsRouteUiSpec(
val title: TextReference,
val shouldApplyHorizontalPadding: Boolean,
val shouldFillHeight: Boolean,
)
internal fun AddFundsModel.UiRoute.uiSpec(): AddFundsRouteUiSpec = when (this) {
AddFundsModel.UiRoute.Loading -> AddFundsRouteUiSpec(
title = resourceReference(R.string.common_add_funds),
shouldApplyHorizontalPadding = false,
shouldFillHeight = false,
)
AddFundsModel.UiRoute.ChooseToken -> AddFundsRouteUiSpec(
title = resourceReference(R.string.common_add_funds),
shouldApplyHorizontalPadding = false,
shouldFillHeight = true,
)
AddFundsModel.UiRoute.UserPortfolio -> AddFundsRouteUiSpec(
title = resourceReference(R.string.common_add_funds),
shouldApplyHorizontalPadding = false,
shouldFillHeight = false,
)
AddFundsModel.UiRoute.TokenActions -> AddFundsRouteUiSpec(
title = resourceReference(CoreR.string.common_get_token),
shouldApplyHorizontalPadding = true,
shouldFillHeight = true,
)
}

View file

@ -23,7 +23,7 @@ import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioFooterKind
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.uiSpec
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
import dev.chrisbanes.haze.rememberHazeState
@Composable

View file

@ -6,7 +6,7 @@ import com.arkivanov.decompose.router.stack.ChildStack
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
@Composable
internal fun AddToPortfolioBottomSheetSwitch(

View file

@ -22,7 +22,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.uiSpec
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
@Composable
internal fun AddToPortfolioBottomSheetV2(
@ -39,6 +39,11 @@ internal fun AddToPortfolioBottomSheetV2(
contentStack.value = stack
}
val type = if (stack.active.configuration is AddToPortfolioRoutes.TokenActions) {
TangemBottomSheetType.Default
} else {
TangemBottomSheetType.Modal
}
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
onBack = onBack,
config = TangemBottomSheetConfig(
@ -46,7 +51,7 @@ internal fun AddToPortfolioBottomSheetV2(
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
type = TangemBottomSheetType.Modal,
type = type,
containerColor = TangemTheme.colors2.surface.level2,
title = {
AddToPortfolioBottomSheetTitle(
@ -86,7 +91,9 @@ private fun AddToPortfolioRouteContent(animatedStack: ChildStack<AddToPortfolioR
Spacer(modifier = Modifier.height(scrollBottomReserve))
}
} else {
animatedStack.active.instance.Content(modifier = baseModifier)
val isFullScreenRoute = animatedStack.active.configuration is AddToPortfolioRoutes.TokenActions
val sizeModifier = if (isFullScreenRoute) Modifier.fillMaxSize() else Modifier
animatedStack.active.instance.Content(modifier = baseModifier.then(sizeModifier))
}
}

View file

@ -15,11 +15,14 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioModel
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.flowOf
@Suppress("LongParameterList")
internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
@ -60,6 +63,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
params = TokenActionsComponent.Params(
callbacks = model,
data = model.tokenActionsData,
bottomAction = flowOf(BottomAction.GoToToken),
),
)
}

View file

@ -92,13 +92,5 @@ internal class PortfolioAnalyticsEvent(
if (source != null) put("Source", source)
},
)
fun getTokenLater() = PortfolioAnalyticsEvent(
event = "Popup Get token - Button Later",
category = category,
params = buildMap {
if (source != null) put("Source", source)
},
)
}
}

View file

@ -4,8 +4,8 @@ import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioCompo
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.impl.addtoportfolio.DefaultAddToPortfolioComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.DefaultAddToPortfolioManager
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.DefaultUserPortfolioComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.userportfolio.DefaultUserPortfolioComponent
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn

View file

@ -5,8 +5,8 @@ import com.tangem.core.decompose.model.Model
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.ChooseNetworkModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.TokenActionsModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioModel
import com.tangem.features.commonfeatures.impl.tokenactions.model.TokenActionsModel
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn

View file

@ -29,18 +29,20 @@ import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.commonfeatures.api.addtoportfolio.*
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ChooseNetworkComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.state.UserPortfolioStateController
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.userportfolio.state.UserPortfolioStateController
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -107,10 +109,6 @@ internal class AddToPortfolioModel @Inject constructor(
startRedesignAddToPortfolioFlow()
}
override fun onQuickActionClick(action: TokenActionsBSContentUM.Action) {
analyticsEventHandler.send(eventBuilder.getTokenActionClick(actionUM = action))
}
private fun <T> replayMutableSharedFlow() = MutableSharedFlow<T>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
@ -155,7 +153,7 @@ internal class AddToPortfolioModel @Inject constructor(
}
}
@Suppress("LongMethod")
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun startRedesignAddToPortfolioFlow() {
channelFlow<Unit> {
fun finishSuccessFlow(result: AddToPortfolioManager.Result) {
@ -312,9 +310,15 @@ internal class AddToPortfolioModel @Inject constructor(
.onEmpty { finishSuccessFlow(result) }
.launchIn(this)
callbackDelegate.onChooseTokenBottomActionClick.receiveAsFlow().first()
analyticsEventHandler.send(eventBuilder.getTokenLater())
finishSuccessFlow(result)
when (val meta = terminalTokenActionsFlow().first()) {
is AddToPortfolioManager.FinishMeta.OnBottomAction -> {
finishSuccessFlow(result.copy(meta = meta))
}
AddToPortfolioManager.FinishMeta.OnQuickAction -> {
finishSuccessFlow(result.copy(meta = meta))
}
AddToPortfolioManager.FinishMeta.None -> Unit
}
}
.catch { throwable ->
TangemLogger.e("Error", throwable)
@ -323,6 +327,21 @@ internal class AddToPortfolioModel @Inject constructor(
.launchIn(modelScope)
}
private fun terminalTokenActionsFlow() = channelFlow {
callbackDelegate.onChooseTokenBottomActionClick.receiveAsFlow()
.onEach { bottomAction ->
channel.send(AddToPortfolioManager.FinishMeta.OnBottomAction(bottomAction))
}
.launchIn(this)
callbackDelegate.onQuickActionClick.receiveAsFlow()
.onEach { (action, shouldDismiss) ->
analyticsEventHandler.send(eventBuilder.getTokenActionClick(actionUM = action))
if (shouldDismiss) channel.send(AddToPortfolioManager.FinishMeta.OnQuickAction)
}
.launchIn(this)
awaitClose()
}
private suspend fun getInitialSelection(
initialData: AvailableToAddData,
): AddToPortfolioInitialSelectionResolver.InitialSelection? {
@ -529,7 +548,8 @@ internal class AddToPortfolioCallbackDelegate @Inject constructor() :
UserPortfolioComponent.Callbacks {
val onNetworkSelected = Channel<TokenMarketInfo.Network>()
val onChooseTokenBottomActionClick = Channel<Unit>()
val onChooseTokenBottomActionClick = Channel<BottomAction>()
val onQuickActionClick = Channel<Pair<TokenActionsBSContentUM.Action, Boolean>>()
val onChangeNetworkClick = Channel<Unit>()
val onChangePortfolioClick = Channel<Unit>()
val onTokenAdded = Channel<CryptoCurrencyStatus>()
@ -539,8 +559,12 @@ internal class AddToPortfolioCallbackDelegate @Inject constructor() :
onNetworkSelected.trySend(network)
}
override fun onBottomActionClick() {
onChooseTokenBottomActionClick.trySend(Unit)
override fun onBottomActionClick(bottomAction: BottomAction) {
onChooseTokenBottomActionClick.trySend(bottomAction)
}
override fun onQuickActionClick(action: TokenActionsBSContentUM.Action, shouldDismiss: Boolean) {
onQuickActionClick.trySend(action to shouldDismiss)
}
override fun onChangeNetworkClick() {

View file

@ -44,7 +44,7 @@ internal fun AddToPortfolioRoutes.uiSpec(): AddToPortfolioRouteUiSpec = when (th
)
AddToPortfolioRoutes.TokenActions -> AddToPortfolioRouteUiSpec(
title = resourceReference(R.string.common_get_token),
isScrollable = true,
isScrollable = false,
shouldApplyHorizontalPadding = true,
footer = AddToPortfolioFooterKind.None,
)

View file

@ -41,7 +41,7 @@ internal class DefaultChooseTokenComponent @AssistedInject constructor(
override fun Content(modifier: Modifier) {
val bottomSheet by bottomSheetSlot.subscribeAsState()
val state by model.state.collectAsStateWithLifecycle()
ChooseTokenScreen(state = state)
ChooseTokenScreen(state = state, modifier = modifier)
bottomSheet.child?.instance?.BottomSheet()
}

View file

@ -6,6 +6,8 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.commonfeatures.api.R
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridgeInternal.SearchQuery
@ -14,10 +16,9 @@ import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult
import com.tangem.features.commonfeatures.impl.choosetoken.converter.SearchBarToggleTransformer
import com.tangem.features.commonfeatures.impl.choosetoken.converter.SearchBarUpdateQueryTransformer
import com.tangem.features.commonfeatures.impl.choosetoken.market.state.SwapMarketState
import com.tangem.features.commonfeatures.impl.choosetoken.ui.ChooseTokenFullUM
import com.tangem.features.commonfeatures.impl.choosetoken.ui.ChooseTokenInitialUM
import com.tangem.features.commonfeatures.api.R
import com.tangem.features.commonfeatures.impl.choosetoken.market.state.SwapMarketState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@ -78,18 +79,10 @@ internal class ChooseTokenModel @Inject constructor(
.onEach { marketBlockDelegate.addToPortfolioSlot.dismiss() }
.launchIn(modelScope)
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { addedResult ->
val isSearched = ChooseTokenAnalyticsPayload.IsSearched(isSearchingState)
val isMarketToken = ChooseTokenAnalyticsPayload.IsMarketTokenSelected(true)
val chooseTokenResult = ChooseTokenResult(
currency = addedResult.addedCurrency,
account = addedResult.account,
wallet = addedResult.wallet,
analyticsPayload = setOf(isSearched, isMarketToken),
)
bridge.onCurrencyChosen(chooseTokenResult)
marketBlockDelegate.addToPortfolioSlot.dismiss()
}
.onEach { notifyCurrencyChosen(it, isMarketTokenSelected = true) }
.launchIn(modelScope)
addToPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { notifyCurrencyChosen(it, isMarketTokenSelected = false) }
.launchIn(modelScope)
}
@ -97,6 +90,20 @@ internal class ChooseTokenModel @Inject constructor(
bridge.onClose()
}
private fun notifyCurrencyChosen(addedResult: AddToPortfolioManager.Result, isMarketTokenSelected: Boolean) {
val chooseTokenResult = ChooseTokenResult(
currency = addedResult.addedCurrency,
account = addedResult.account,
wallet = addedResult.wallet,
analyticsPayload = setOf(
ChooseTokenAnalyticsPayload.IsSearched(isSearchingState),
ChooseTokenAnalyticsPayload.IsMarketTokenSelected(isMarketTokenSelected),
),
)
bridge.onCurrencyChosen(chooseTokenResult)
marketBlockDelegate.addToPortfolioSlot.dismiss()
}
private fun getInitialSearchBar(): SearchBarUM = SearchBarUM(
placeholderText = resourceReference(R.string.common_search),
query = "",
@ -112,6 +119,7 @@ internal class ChooseTokenModel @Inject constructor(
private fun getInitState() = ChooseTokenInitialUM(
screenTitle = bridge.settings.title,
isAppBarShown = bridge.settings.isAppBarShown,
onCloseClick = ::onBackClicked,
searchBar = getInitialSearchBar(),
)

View file

@ -82,12 +82,14 @@ private val ChooseTokenFullUM.isEmptyState: Boolean
internal fun ChooseTokenScreen(state: ChooseTokenFullUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(color = TangemTheme.colors.background.secondary)
.background(color = TangemTheme.colors2.surface.level2)
.fillMaxSize()
.imePadding(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBar(title = state.initialUM.screenTitle, onBackClick = state.initialUM.onCloseClick, Modifier)
if (state.initialUM.isAppBarShown) {
AppBar(title = state.initialUM.screenTitle, onBackClick = state.initialUM.onCloseClick, Modifier)
}
Content(
state = state,
@ -465,6 +467,7 @@ private val wallets
private val initialUM = ChooseTokenInitialUM(
screenTitle = stringReference("Choose token"),
isAppBarShown = true,
onCloseClick = {},
searchBar = searchBar,
)

View file

@ -13,6 +13,7 @@ internal data class ChooseTokenFullUM(
internal data class ChooseTokenInitialUM(
val screenTitle: TextReference,
val isAppBarShown: Boolean,
val onCloseClick: () -> Unit,
val searchBar: SearchBarUM,
)

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio
package com.tangem.features.commonfeatures.impl.tokenactions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -18,14 +18,16 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.TokenActionsModel
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.TokenActionsContent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.TokenActionsContentV2
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.commonfeatures.impl.tokenactions.model.TokenActionsModel
import com.tangem.features.commonfeatures.impl.tokenactions.ui.TokenActionsContent
import com.tangem.features.commonfeatures.impl.tokenactions.ui.TokenActionsContentV2
import com.tangem.features.tokenreceive.TokenReceiveComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
internal class TokenActionsComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@ -74,15 +76,14 @@ internal class TokenActionsComponent @AssistedInject constructor(
data class Params(
val data: Flow<CryptoCurrencyData>,
val callbacks: Callbacks,
val bottomAction: BottomAction = BottomAction.Later,
val bottomAction: Flow<BottomAction> = flowOf(BottomAction.None),
val isRedesignForced: Boolean = false,
val isCompact: Boolean = false,
)
enum class BottomAction { Later, GoToToken }
interface Callbacks {
fun onBottomActionClick()
fun onQuickActionClick(action: TokenActionsBSContentUM.Action) {}
fun onBottomActionClick(bottomAction: BottomAction)
fun onQuickActionClick(action: TokenActionsBSContentUM.Action, shouldDismiss: Boolean) {}
}
@AssistedFactory

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.model
package com.tangem.features.commonfeatures.impl.tokenactions.model
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
@ -12,8 +12,8 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -45,7 +45,9 @@ internal class TokenActionsModel @Inject constructor(
private val tokenActionsHandler: TokenActionsHandler =
tokenActionsIntentsFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
onHandleQuickAction = { handledAction -> handledQuickAction(handledAction) },
onHandleQuickAction = { handledAction, shouldDismiss ->
handledQuickAction(handledAction, shouldDismiss)
},
)
val bottomSheetNavigation: SlotNavigation<TokenReceiveConfig> = SlotNavigation()
@ -55,15 +57,17 @@ internal class TokenActionsModel @Inject constructor(
combine(
params.data,
getBalanceHidingSettingsUseCase.isBalanceHidden(),
) { cryptoCurrencyData, isBalanceHidden ->
cryptoCurrencyData to isBalanceHidden
params.bottomAction,
) { cryptoCurrencyData, isBalanceHidden, bottomAction ->
Triple(cryptoCurrencyData, isBalanceHidden, bottomAction)
}
.mapLatest { (cryptoCurrencyData, isBalanceHidden) ->
.mapLatest { (cryptoCurrencyData, isBalanceHidden, bottomAction) ->
uiBuilder.build(
cryptoCurrencyData = cryptoCurrencyData,
tokenActionsHandler = tokenActionsHandler,
appCurrency = currentAppCurrency.value,
isBalanceHidden = isBalanceHidden,
bottomAction = bottomAction,
)
}
.flowOn(dispatchers.default)
@ -73,16 +77,18 @@ internal class TokenActionsModel @Inject constructor(
initialValue = null,
)
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) = modelScope.launch {
params.callbacks.onQuickActionClick(handledAction.action)
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
if (!isReceive) return@launch
val tokenConfig = withContext(dispatchers.default) {
receiveAddressesFactory.create(
status = handledAction.cryptoCurrencyData.status,
userWalletId = handledAction.cryptoCurrencyData.userWallet.walletId,
)
} ?: return@launch
bottomSheetNavigation.activate(tokenConfig)
}
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction, shouldDismiss: Boolean) =
modelScope.launch {
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
if (isReceive) {
val tokenConfig = withContext(dispatchers.default) {
receiveAddressesFactory.create(
status = handledAction.cryptoCurrencyData.status,
userWalletId = handledAction.cryptoCurrencyData.userWallet.walletId,
)
}
if (tokenConfig != null) bottomSheetNavigation.activate(tokenConfig)
}
params.callbacks.onQuickActionClick(handledAction.action, shouldDismiss)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.model
package com.tangem.features.commonfeatures.impl.tokenactions.model
import androidx.compose.ui.text.SpanStyle
import com.tangem.common.getTotalCryptoAmount
@ -11,6 +11,7 @@ import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToI
import com.tangem.common.ui.markets.action.CryptoCurrencyData
import com.tangem.common.ui.markets.action.QuickActionsConverter.quickActions
import com.tangem.common.ui.markets.action.TokenActionsHandler
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.ParamsContainer
@ -30,9 +31,9 @@ import com.tangem.features.commonfeatures.impl.R
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.PortfolioBadgeUM
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.PortfolioBadgeUM
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM
import java.math.BigDecimal
import javax.inject.Inject
@ -50,6 +51,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler: TokenActionsHandler,
appCurrency: AppCurrency,
isBalanceHidden: Boolean,
bottomAction: BottomAction,
): TokenActionsUM {
return if (designFeatureToggles.isRedesignEnabled || params.isRedesignForced) {
buildV2(
@ -57,11 +59,13 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
appCurrency = appCurrency,
isBalanceHidden = isBalanceHidden,
bottomAction = bottomAction,
)
} else {
buildV1(
cryptoCurrencyData = cryptoCurrencyData,
tokenActionsHandler = tokenActionsHandler,
bottomAction = bottomAction,
)
}
}
@ -69,6 +73,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
private fun buildV1(
cryptoCurrencyData: CryptoCurrencyData,
tokenActionsHandler: TokenActionsHandler,
bottomAction: BottomAction,
): TokenActionsUM {
val status = cryptoCurrencyData.status
val tokenUM = TokenItemState.Content(
@ -88,9 +93,9 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
isRedesignEnabled = false,
),
bottomActionText = bottomActionText(params.bottomAction),
bottomActionText = bottomActionText(bottomAction),
onBottomActionClick = {
params.callbacks.onBottomActionClick()
params.callbacks.onBottomActionClick(bottomAction)
},
)
}
@ -100,6 +105,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler: TokenActionsHandler,
appCurrency: AppCurrency,
isBalanceHidden: Boolean,
bottomAction: BottomAction,
): TokenActionsUM {
val status = cryptoCurrencyData.status
val tokenUM = TokenItemState.Content(
@ -119,19 +125,20 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
isRedesignEnabled = true,
),
bottomActionText = bottomActionText(params.bottomAction),
bottomActionText = bottomActionText(bottomAction),
onBottomActionClick = {
params.callbacks.onBottomActionClick()
params.callbacks.onBottomActionClick(bottomAction)
},
isBalancesHidden = isBalanceHidden,
portfolioBadge = createPortfolioBadge(cryptoCurrencyData = cryptoCurrencyData),
isCompact = params.isCompact,
)
}
private fun bottomActionText(action: TokenActionsComponent.BottomAction): TextReference {
private fun bottomActionText(action: BottomAction): TextReference? {
return when (action) {
TokenActionsComponent.BottomAction.Later -> resourceReference(R.string.common_later)
TokenActionsComponent.BottomAction.GoToToken -> resourceReference(R.string.common_go_to_token)
BottomAction.GoToToken -> resourceReference(R.string.common_go_to_token)
BottomAction.None -> null
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.ui
package com.tangem.features.commonfeatures.impl.tokenactions.ui
import android.content.res.Configuration
import androidx.compose.foundation.ExperimentalFoundationApi
@ -39,7 +39,7 @@ 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.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM
import kotlinx.collections.immutable.persistentListOf
import java.util.UUID
@ -73,13 +73,15 @@ internal fun TokenActionsContent(state: TokenActionsUM, modifier: Modifier = Mod
}
}
SpacerH16()
if (state.bottomActionText != null) {
SpacerH16()
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = state.bottomActionText.resolveReference(),
onClick = state.onBottomActionClick,
)
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = state.bottomActionText.resolveReference(),
onClick = state.onBottomActionClick,
)
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.ui
package com.tangem.features.commonfeatures.impl.tokenactions.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
@ -43,8 +43,8 @@ import com.tangem.core.ui.format.bigdecimal.formatStyled
import com.tangem.core.ui.format.bigdecimal.price
import com.tangem.core.ui.res.*
import com.tangem.features.commonfeatures.impl.R
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.PortfolioBadgeUM
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.PortfolioBadgeUM
import com.tangem.features.commonfeatures.impl.tokenactions.ui.state.TokenActionsUM
import dev.chrisbanes.haze.rememberHazeState
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
@ -52,53 +52,84 @@ import java.util.UUID
@Composable
internal fun TokenActionsContentV2(state: TokenActionsUM, modifier: Modifier = Modifier) {
if (state.isCompact) {
CompactLayout(state, modifier)
} else {
FullLayout(state, modifier)
}
}
@Composable
private fun CompactLayout(state: TokenActionsUM, modifier: Modifier = Modifier) {
Column(modifier = modifier.fillMaxWidth()) {
QuickActionsList(state)
SpacerH(TangemTheme.dimens2.x4)
}
}
@Composable
private fun FullLayout(state: TokenActionsUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
TokenHeader(
addedToken = state.token,
portfolioBadge = state.portfolioBadge,
isBalanceHidden = state.isBalancesHidden,
)
SpacerH(TangemTheme.dimens2.x2)
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
state.quickActions.actions.fastForEach { actionUM ->
key(actionUM.title) {
val transitionState = remember {
MutableTransitionState(initialState = false).apply { targetState = true }
}
AnimatedVisibility(
visibleState = transitionState,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
TokenActionRow(
iconRes = actionUM.icon,
title = actionUM.title,
description = actionUM.description,
onClick = { state.quickActions.onQuickActionClick(actionUM) },
onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) }
.takeIf { actionUM.isLongClickAvailable },
)
}
}
TokenHeader(
addedToken = state.token,
portfolioBadge = state.portfolioBadge,
isBalanceHidden = state.isBalancesHidden,
)
}
QuickActionsList(state)
val bottomText = state.bottomActionText
if (bottomText != null) {
SpacerH(TangemTheme.dimens2.x6)
CompositionLocalProvider(LocalHazeState provides rememberHazeState()) {
SecondaryTangemButton(
modifier = Modifier.fillMaxWidth(),
onClick = state.onBottomActionClick,
text = bottomText,
size = TangemButtonSize.X12,
shape = TangemButtonShape.Rounded,
)
}
}
SpacerH(TangemTheme.dimens2.x4)
}
}
SpacerH(TangemTheme.dimens2.x6)
CompositionLocalProvider(LocalHazeState provides rememberHazeState()) {
SecondaryTangemButton(
modifier = Modifier.fillMaxWidth(),
onClick = state.onBottomActionClick,
text = state.bottomActionText,
size = TangemButtonSize.X12,
shape = TangemButtonShape.Rounded,
)
@Composable
private fun QuickActionsList(state: TokenActionsUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
) {
state.quickActions.actions.fastForEach { actionUM ->
key(actionUM.title) {
val transitionState = remember {
MutableTransitionState(initialState = false).apply { targetState = true }
}
AnimatedVisibility(
visibleState = transitionState,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
) {
TokenActionRow(
iconRes = actionUM.icon,
title = actionUM.title,
description = actionUM.description,
onClick = { state.quickActions.onQuickActionClick(actionUM) },
onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) }
.takeIf { actionUM.isLongClickAvailable },
)
}
}
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state
package com.tangem.features.commonfeatures.impl.tokenactions.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.markets.action.QuickActions
@ -10,10 +10,11 @@ import com.tangem.core.ui.extensions.TextReference
internal data class TokenActionsUM(
val token: TokenItemState,
val quickActions: QuickActions,
val bottomActionText: TextReference,
val bottomActionText: TextReference?,
val onBottomActionClick: () -> Unit,
val isBalancesHidden: Boolean = false,
val portfolioBadge: PortfolioBadgeUM = PortfolioBadgeUM.None,
val isCompact: Boolean = false,
)
@Immutable

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio
package com.tangem.features.commonfeatures.impl.userportfolio
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -7,7 +7,7 @@ import com.tangem.common.ui.markets.tokenselector.TokenSelectorEmbeddedContent
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.LocalTangemBottomSheetContentBottomInset
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioModel
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

View file

@ -1,8 +1,8 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio
package com.tangem.features.commonfeatures.impl.userportfolio
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
import kotlinx.coroutines.flow.StateFlow
internal interface UserPortfolioComponent : ComposableContentComponent {

View file

@ -1,9 +1,9 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model
package com.tangem.features.commonfeatures.impl.userportfolio.model
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.UserPortfolioComponent
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model
package com.tangem.features.commonfeatures.impl.userportfolio.model
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.markets.tokenselector.TokenSelectorContentUM

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.state
package com.tangem.features.commonfeatures.impl.userportfolio.state
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -7,8 +7,8 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddData
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.transformer.UserPortfolioSectionsTransformer
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.transformer.UserPortfolioSectionsTransformer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

View file

@ -1,4 +1,4 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.transformer
package com.tangem.features.commonfeatures.impl.userportfolio.transformer
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.common.ui.account.toUM
@ -21,7 +21,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddData
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.model.UserPortfolioUM
import com.tangem.features.commonfeatures.impl.userportfolio.model.UserPortfolioUM
import com.tangem.utils.StringsSigns
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.toImmutableList

View file

@ -22,11 +22,13 @@ import com.tangem.features.promobanners.api.PromoBannersBlockComponent
import kotlinx.serialization.Serializable
import javax.inject.Inject
@Suppress("LongParameterList")
internal class FeedEntryChildFactory @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val portfolioComponentFactory: MarketsPortfolioComponent.Factory,
private val portfolioBlockComponentFactory: PortfolioBlockComponent.Factory,
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
private val addFundsComponentFactory: com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent.Factory,
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
private val designFeatureToggles: DesignFeatureToggles,
) {
@ -81,6 +83,7 @@ internal class FeedEntryChildFactory @Inject constructor(
portfolioBlockComponentFactory = portfolioBlockComponentFactory,
designFeatureToggles = designFeatureToggles,
addToPortfolioComponentFactory = addToPortfolioComponentFactory,
addFundsComponentFactory = addFundsComponentFactory,
)
}
is Child.TokenList -> {

View file

@ -0,0 +1,10 @@
package com.tangem.features.feed.components.market.details
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.models.currency.CryptoCurrency
import kotlinx.serialization.Serializable
@Serializable
internal data class AddFundsSlotRoute(
val rawCurrencyId: CryptoCurrency.RawID,
) : Route

View file

@ -19,6 +19,7 @@ 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.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
@ -41,6 +42,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.PreselectedTokenDetailsSection
import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent
@ -64,6 +66,7 @@ internal class DefaultMarketsTokenDetailsComponent(
portfolioBlockComponentFactory: PortfolioBlockComponent.Factory,
val params: Params,
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
// applying l2 compatibility
@ -101,6 +104,10 @@ internal class DefaultMarketsTokenDetailsComponent(
override fun openAddToPortfolioViaUserPortfolio(rawCurrencyId: CryptoCurrency.RawID) {
model.openAddToPortfolioViaUserPortfolio()
}
override fun openAddFunds(rawCurrencyId: CryptoCurrency.RawID) {
model.openAddFunds(rawCurrencyId)
}
},
)
} else {
@ -114,6 +121,14 @@ internal class DefaultMarketsTokenDetailsComponent(
childFactory = ::addToPortfolioChild,
)
private val addFundsSlot = childSlot(
source = model.addFundsSheetNavigation,
serializer = AddFundsSlotRoute.serializer(),
key = "addFundsSlot",
handleBackButton = false,
childFactory = ::addFundsChild,
)
init {
componentScope.launch(dispatchers.default) {
model.networksState.collectLatest { state ->
@ -155,6 +170,20 @@ internal class DefaultMarketsTokenDetailsComponent(
)
}
private fun addFundsChild(
config: AddFundsSlotRoute,
componentContext: ComponentContext,
): ComposableBottomSheetComponent {
val launchMode = AddFundsComponent.LaunchMode.FilteredByRawId(rawCurrencyId = config.rawCurrencyId)
return addFundsComponentFactory.create(
context = childByContext(componentContext),
params = AddFundsComponent.Params(
launchMode = launchMode,
onDismiss = { model.addFundsSheetNavigation.dismiss() },
),
)
}
@Composable
override fun Title(bottomSheetState: State<BottomSheetState>) {
val state by model.state.collectAsStateWithLifecycle()
@ -226,6 +255,7 @@ internal class DefaultMarketsTokenDetailsComponent(
}
val state by model.state.collectAsStateWithLifecycle()
val bottomSheet by addToPortfolioSlot.subscribeAsState()
val addFundsBs by addFundsSlot.subscribeAsState()
val bsState by bottomSheetState
LaunchedEffect(bsState) {
model.isVisibleOnScreen.value = bsState == BottomSheetState.EXPANDED
@ -248,6 +278,7 @@ internal class DefaultMarketsTokenDetailsComponent(
},
)
bottomSheet.child?.instance?.BottomSheet()
addFundsBs.child?.instance?.BottomSheet()
}
@Serializable

View file

@ -5,6 +5,8 @@ import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
import com.tangem.common.ui.markets.action.TokenActionsHandler
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -36,6 +38,7 @@ internal class MarketsPortfolioModel @Inject constructor(
private val tokenActionsHandlerFactory: TokenActionsHandler.Factory,
private val receiveAddressesFactory: ReceiveAddressesFactory,
private val analyticsEventHandler: AnalyticsEventHandler,
private val appRouter: AppRouter,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
@ -68,6 +71,17 @@ internal class MarketsPortfolioModel @Inject constructor(
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(modelScope)
addToPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { result ->
bottomSheetNavigation.dismiss()
appRouter.push(
AppRoute.CurrencyDetails(
userWalletId = result.wallet.walletId,
currency = result.addedCurrency.currency,
),
)
}
.launchIn(modelScope)
}
fun setTokenNetworks(networks: List<TokenMarketInfo.Network>) {
@ -127,7 +141,7 @@ internal class MarketsPortfolioModel @Inject constructor(
private fun createTokenActionsHandler(): TokenActionsHandler {
return tokenActionsHandlerFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
onHandleQuickAction = { handledAction ->
onHandleQuickAction = { handledAction, _ ->
val currency = handledAction.cryptoCurrencyData.status.currency
analyticsEventHandler.send(
analyticsEventBuilder.quickActionClick(

View file

@ -5,4 +5,5 @@ import com.tangem.domain.models.currency.CryptoCurrency
internal interface PortfolioBlockParentClickIntents {
fun openAddToPortfolioDirect()
fun openAddToPortfolioViaUserPortfolio(rawCurrencyId: CryptoCurrency.RawID)
fun openAddFunds(rawCurrencyId: CryptoCurrency.RawID)
}

View file

@ -171,7 +171,7 @@ internal class PortfolioBlockModel @Inject constructor(
tokenSymbol = firstCurrency.symbol,
isBalanceHidden = isBalanceHidden,
onRowClick = { parentRouter?.openAddToPortfolioViaUserPortfolio(currencyRawId) },
onAddFundsClick = {},
onAddFundsClick = { parentRouter?.openAddFunds(currencyRawId) },
)
}
}

View file

@ -48,6 +48,8 @@ import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.tokenactions.BottomAction
import com.tangem.features.feed.components.market.details.AddFundsSlotRoute
import com.tangem.features.feed.components.market.details.AddToPortfolioSlotRoute
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
import com.tangem.features.feed.components.market.details.analytics.MarketTokenAnalyticsEvent
@ -233,6 +235,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
val networksState = MutableStateFlow<TokenNetworksState>(TokenNetworksState.Loading)
val addToPortfolioSheetNavigation = SlotNavigation<AddToPortfolioSlotRoute>()
val addFundsSheetNavigation = SlotNavigation<AddFundsSlotRoute>()
private val isAddToPortfolioAvailable: Boolean =
params.shouldShowPortfolio && designFeatureToggles.isRedesignEnabled
@ -345,7 +348,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
.onEach { addToPortfolioSheetNavigation.dismiss() }
.launchIn(modelScope)
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { addToPortfolioSheetNavigation.dismiss() }
.onEach { result ->
addToPortfolioSheetNavigation.dismiss()
val meta = result.meta
if (meta is AddToPortfolioManager.FinishMeta.OnBottomAction &&
meta.action == BottomAction.GoToToken
) {
openTokenDetails(result)
}
}
.launchIn(modelScope)
addToPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { result ->
@ -367,6 +378,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
addToPortfolioSheetNavigation.activate(AddToPortfolioSlotRoute)
}
fun openAddFunds(rawCurrencyId: com.tangem.domain.models.currency.CryptoCurrency.RawID) {
addFundsSheetNavigation.activate(AddFundsSlotRoute(rawCurrencyId = rawCurrencyId))
}
private fun openTokenDetails(result: AddToPortfolioManager.Result) {
appRouter.push(
AppRoute.CurrencyDetails(

View file

@ -112,6 +112,7 @@ dependencies {
implementation(projects.features.sendV2.api)
implementation(projects.features.tokenRecieve.api)
implementation(projects.features.yieldSupply.api)
implementation(projects.features.commonFeatures.api)
implementation(deps.decompose.ext.compose)

View file

@ -21,8 +21,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDeta
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreenLegacy
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.AddFundsBottomSheetComponent
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.ChooseAddressBottomSheetComponent
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.CloreMigrationBottomSheetComponent
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.DynamicAddressesBottomSheetComponent
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.TransferBottomSheetComponent
@ -47,6 +47,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
expressTransactionsComponentFactory: ExpressTransactionsComponent.Factory,
private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory,
private val yieldSupplyWarningComponentFactory: YieldSupplyDepositedWarningComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
yieldSupplyComponentFactory: YieldSupplyComponent.Factory,
private val ratingComponentFactory: RatingComponent.Factory,
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
@ -177,9 +178,15 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
dynamicAddressesDelegate = model.dynamicAddressesDelegate,
onDismiss = model.bottomSheetNavigation::dismiss,
)
is TokenDetailsBottomSheetConfig.AddFunds -> AddFundsBottomSheetComponent(
stateFlow = model.addFundsUiState,
onDismiss = model.bottomSheetNavigation::dismiss,
is TokenDetailsBottomSheetConfig.AddFunds -> addFundsComponentFactory.create(
context = childByContext(componentContext),
params = AddFundsComponent.Params(
launchMode = AddFundsComponent.LaunchMode.TokenActionsOnly(
userWalletId = route.userWalletId,
currency = route.currency,
),
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
is TokenDetailsBottomSheetConfig.Transfer -> TransferBottomSheetComponent(
stateFlow = model.transferUiState,

View file

@ -559,7 +559,12 @@ internal class TokenDetailsModel @Inject constructor(
}
override fun onAddFundsClick() {
bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.AddFunds)
bottomSheetNavigation.activate(
TokenDetailsBottomSheetConfig.AddFunds(
userWalletId = userWalletId,
currency = cryptoCurrency,
),
)
}
override fun onTransferClick() {

View file

@ -4,6 +4,7 @@ import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.model.details.TokenAction
import kotlinx.serialization.Serializable
@ -32,7 +33,10 @@ sealed class TokenDetailsBottomSheetConfig : Route {
data object DynamicAddresses : TokenDetailsBottomSheetConfig()
@Serializable
data object AddFunds : TokenDetailsBottomSheetConfig()
data class AddFunds(
val userWalletId: UserWalletId,
val currency: CryptoCurrency,
) : TokenDetailsBottomSheetConfig()
@Serializable
data object Transfer : TokenDetailsBottomSheetConfig()

View file

@ -1,59 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM
import kotlinx.coroutines.flow.StateFlow
import com.tangem.core.ui.R as CoreR
internal class AddFundsBottomSheetComponent(
private val stateFlow: StateFlow<AddFundsUM>,
private val onDismiss: () -> Unit,
) : ComposableBottomSheetComponent {
override fun dismiss() {
onDismiss()
}
@Composable
override fun BottomSheet() {
val state by stateFlow.collectAsStateWithLifecycle()
val config = remember(state) {
TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = state,
)
}
TangemModalBottomSheet<AddFundsUM>(
config = config,
containerColor = TangemTheme.colors2.surface.level2,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(CoreR.string.common_get_token),
endIconRes = CoreR.drawable.ic_close_24,
onEndClick = ::dismiss,
)
},
content = { contentState ->
AddFundsBottomSheetContent(
state = contentState,
onCloseClick = ::dismiss,
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4),
)
},
)
}
}

View file

@ -1,167 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
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 com.tangem.common.ui.tokenaction.TokenActionRow
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.ds.button.SecondaryTangemButton
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonSize
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.LocalHazeState
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM
import dev.chrisbanes.haze.rememberHazeState
import com.tangem.core.ui.R as CoreR
@Composable
internal fun AddFundsBottomSheetContent(state: AddFundsUM, onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
) {
BuyActionRow(state = state)
SwapActionRow(state = state)
ReceiveActionRow(state = state)
SpacerH(TangemTheme.dimens2.x2)
CompositionLocalProvider(LocalHazeState provides rememberHazeState()) {
SecondaryTangemButton(
modifier = Modifier.fillMaxWidth(),
onClick = onCloseClick,
text = resourceReference(CoreR.string.common_close),
size = TangemButtonSize.X12,
shape = TangemButtonShape.Rounded,
)
}
SpacerH(TangemTheme.dimens2.x4)
}
}
@Composable
private fun BuyActionRow(state: AddFundsUM) {
val row = (state as? AddFundsUM.Content)?.buy
if (state is AddFundsUM.Content && row == null) return
ActionRow(
iconRes = CoreR.drawable.ic_credit_card_20,
title = resourceReference(CoreR.string.common_buy),
description = resourceReference(CoreR.string.quick_action_buy_description),
row = row,
isLoading = state is AddFundsUM.Loading,
)
}
@Composable
private fun SwapActionRow(state: AddFundsUM) {
val row = (state as? AddFundsUM.Content)?.swap
if (state is AddFundsUM.Content && row == null) return
ActionRow(
iconRes = CoreR.drawable.ic_exchange_mini_24,
title = resourceReference(CoreR.string.common_swap),
description = resourceReference(CoreR.string.quick_action_swap_description),
row = row,
isLoading = state is AddFundsUM.Loading,
)
}
@Composable
private fun ReceiveActionRow(state: AddFundsUM) {
val row = (state as? AddFundsUM.Content)?.receive
if (state is AddFundsUM.Content && row == null) return
ActionRow(
iconRes = CoreR.drawable.ic_qrcode_new_24,
title = resourceReference(CoreR.string.common_receive),
description = resourceReference(CoreR.string.quick_action_receive_description),
row = row,
isLoading = state is AddFundsUM.Loading,
)
}
@Composable
private fun ActionRow(
iconRes: Int,
title: TextReference,
description: TextReference,
row: AddFundsUM.Row?,
isLoading: Boolean,
) {
if (isLoading || row?.isLoading == true) {
TokenActionRow(
iconRes = iconRes,
title = title,
description = description,
tailContent = { TailLoader() },
)
} else {
TokenActionRow(
iconRes = iconRes,
title = title,
description = description,
onClick = row?.onClick,
onLongClick = row?.onLongClick,
isEnabled = row?.isEnabled == true,
)
}
}
@Composable
private fun TailLoader() {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
color = TangemTheme.colors2.graphic.neutral.tertiary,
strokeWidth = 2.dp,
)
}
// region Preview
@Preview(widthDp = 360, showBackground = true)
@Composable
private fun Preview(@PreviewParameter(AddFundsPreviewProvider::class) state: AddFundsUM) {
TangemThemePreviewRedesign {
CompositionLocalProvider(LocalRedesignEnabled provides true) {
AddFundsBottomSheetContent(
state = state,
onCloseClick = {},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
}
}
private class AddFundsPreviewProvider : PreviewParameterProvider<AddFundsUM> {
override val values: Sequence<AddFundsUM> = sequenceOf(
AddFundsUM.Loading,
AddFundsUM.Content(
buy = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}),
swap = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}),
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
),
AddFundsUM.Content(
buy = AddFundsUM.Row(isLoading = false, isEnabled = false, onClick = {}),
swap = AddFundsUM.Row(isLoading = false, isEnabled = false, onClick = {}),
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
),
AddFundsUM.Content(
buy = null,
swap = null,
receive = AddFundsUM.Row(isLoading = false, isEnabled = true, onClick = {}, onLongClick = {}),
),
)
}
// endregion

View file

@ -25,6 +25,7 @@ import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.tokens.model.details.TokenAction
import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent
import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent
@ -67,6 +68,7 @@ internal class WalletComponent @AssistedInject constructor(
private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory,
private val tokenActionsComponentFactory: TokenActionsComponent.Factory,
private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
private val designFeatureToggles: DesignFeatureToggles,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
@ -180,6 +182,15 @@ internal class WalletComponent @AssistedInject constructor(
),
)
}
is WalletDialogConfig.AddFunds -> {
addFundsComponentFactory.create(
context = childByContext(componentContext),
params = AddFundsComponent.Params(
launchMode = AddFundsComponent.LaunchMode.ChooseToken(dialogConfig.userWalletId),
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
),
)
}
is WalletDialogConfig.AddAndManage -> {
AddAndManageBottomSheetComponent(
appComponentContext = childByContext(componentContext),

View file

@ -120,7 +120,9 @@ internal class DefaultWalletRouter @Inject constructor(
}
override fun openAddFunds(userWalletId: UserWalletId) {
router.push(AppRoute.AddFunds(userWalletId = userWalletId))
dialogNavigation.activate(
configuration = WalletDialogConfig.AddFunds(userWalletId = userWalletId),
)
}
override fun isWalletLastScreen(): Boolean {

View file

@ -62,6 +62,9 @@ internal sealed interface WalletDialogConfig {
val customerId: String,
) : WalletDialogConfig
@Serializable
data class AddFunds(val userWalletId: UserWalletId) : WalletDialogConfig
@Serializable
data class OrganizeTokens(val userWalletId: UserWalletId) : WalletDialogConfig