Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-18 17:45:13 +05:00
parent a13ace780e
commit d42cb2a79e
30 changed files with 397 additions and 24 deletions

View file

@ -9,6 +9,7 @@ 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
@ -111,6 +112,7 @@ internal class ChildFactory @Inject constructor(
private val kycComponentFactory: KycComponent.Factory,
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
) {
@Suppress("LongMethod", "CyclomaticComplexMethod")
@ -227,6 +229,13 @@ 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

@ -58,6 +58,11 @@ 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

@ -74,5 +74,9 @@
{
"name": "TWI_1403_PUSH_NOTIFICATION_SETTINGS_ENABLED",
"version": "undefined"
},
{
"name": "AND_15310_ADD_FUNDS_STAGE1",
"version": "undefined"
}
]

View file

@ -0,0 +1,14 @@
package com.tangem.features.commonfeatures.api.addfunds
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.wallet.UserWalletId
interface AddFundsComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
)
interface Factory : ComponentFactory<Params, AddFundsComponent>
}

View file

@ -41,6 +41,11 @@ interface ChooseTokenBridge : ChooseTokenBridgeInternal {
isShowMarketBlock = true,
isShowPaymentAccount = true,
)
val AddFunds = Settings(
title = resourceReference(R.string.swapping_to_title),
isShowMarketBlock = true,
isShowPaymentAccount = false,
)
}
}

View file

@ -0,0 +1,108 @@
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.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
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.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.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.addtoportfolio.analytics.PortfolioAnalyticsEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultAddFundsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: AddFundsComponent.Params,
chooseTokenComponentFactory: ChooseTokenComponent.Factory,
tokenActionsComponentFactory: TokenActionsComponent.Factory,
) : 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 tokenActionsComponent: TokenActionsComponent = tokenActionsComponentFactory.create(
context = child(key = "addFundsTokenActions"),
params = TokenActionsComponent.Params(
eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
tokenSymbol = "",
source = ANALYTICS_SOURCE,
category = ANALYTICS_CATEGORY,
),
data = model.tokenActionsData,
callbacks = model,
bottomAction = TokenActionsComponent.BottomAction.GoToToken,
isRedesignForced = true,
),
)
@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)
}
},
)
}
}
}
@AssistedFactory
interface Factory : AddFundsComponent.Factory {
override fun create(context: AppComponentContext, params: AddFundsComponent.Params): DefaultAddFundsComponent
}
private companion object {
const val ANALYTICS_SOURCE = "AddFunds"
const val ANALYTICS_CATEGORY = "Add Funds"
}
}

View file

@ -0,0 +1,16 @@
package com.tangem.features.commonfeatures.impl.addfunds.di
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
import com.tangem.features.commonfeatures.impl.addfunds.DefaultAddFundsComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
internal interface AddFundsComponentModule {
@Binds
fun bindAddFundsComponentFactory(factory: DefaultAddFundsComponent.Factory): AddFundsComponent.Factory
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.commonfeatures.impl.addfunds.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(ModelComponent::class)
internal interface AddFundsModelModule {
@Binds
@IntoMap
@ClassKey(AddFundsModel::class)
fun addFundsModel(model: AddFundsModel): Model
}

View file

@ -0,0 +1,103 @@
package com.tangem.features.commonfeatures.impl.addfunds.model
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.markets.action.CryptoCurrencyData
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.usecase.GetCryptoCurrencyActionsUseCaseV2
import com.tangem.domain.models.account.AccountStatus
import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent
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.impl.addtoportfolio.TokenActionsComponent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@ModelScoped
internal class AddFundsModel @Inject constructor(
paramsContainer: ParamsContainer,
chooseTokenBridgeFactory: ChooseTokenBridge.Factory,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
private val appRouter: AppRouter,
override val dispatchers: CoroutineDispatcherProvider,
) : Model(), TokenActionsComponent.Callbacks {
private val params = paramsContainer.require<AddFundsComponent.Params>()
val chooseTokenBridge: ChooseTokenBridge = chooseTokenBridgeFactory.create(
modelScope = modelScope,
settings = ChooseTokenBridge.Settings.AddFunds,
analyticsPayload = setOf(
ChooseTokenAnalyticsPayload.ScreensSources(SCREEN_SOURCE),
),
)
private val selectedToken = MutableStateFlow<ChooseTokenResult?>(null)
val isTokenActionsShown: StateFlow<Boolean> = selectedToken
.map { it != null }
.stateIn(modelScope, SharingStarted.Eagerly, initialValue = false)
@OptIn(ExperimentalCoroutinesApi::class)
val tokenActionsData: Flow<CryptoCurrencyData> = selectedToken
.filterNotNull()
.flatMapLatest { result ->
val cryptoPortfolio = result.account as? AccountStatus.CryptoPortfolio
?: return@flatMapLatest emptyFlow()
getCryptoCurrencyActionsUseCase(
accountId = cryptoPortfolio.account.accountId,
currency = result.currency.currency,
).map { actionsState ->
CryptoCurrencyData(
userWallet = result.wallet,
status = result.currency,
actions = actionsState.states,
isAccountMode = false,
account = cryptoPortfolio,
)
}
}
init {
chooseTokenBridge.selectWalletTab(params.userWalletId)
observeBridge()
}
override fun onBottomActionClick() {
val result = selectedToken.value ?: return
selectedToken.value = null
appRouter.replaceCurrent(
AppRoute.CurrencyDetails(
userWalletId = result.wallet.walletId,
currency = result.currency.currency,
),
)
}
fun onTokenActionsDismiss() {
selectedToken.value = null
}
private fun observeBridge() {
modelScope.launch {
chooseTokenBridge.onCurrencyChosen.receiveAsFlow().collect { result ->
selectedToken.value = result
}
}
modelScope.launch {
chooseTokenBridge.onClose.receiveAsFlow().collect {
appRouter.pop()
}
}
}
private companion object {
const val SCREEN_SOURCE = "AddFunds"
}
}

View file

@ -46,7 +46,7 @@ internal class TokenActionsComponent @AssistedInject constructor(
val state = model.uiState.collectAsStateWithLifecycle()
val bottomSheet by bottomSheetSlot.subscribeAsState()
val tokenActionsUM = state.value ?: return
if (LocalRedesignEnabled.current) {
if (LocalRedesignEnabled.current || params.isRedesignForced) {
TokenActionsContentV2(
modifier = modifier,
state = tokenActionsUM,
@ -75,10 +75,14 @@ internal class TokenActionsComponent @AssistedInject constructor(
val eventBuilder: PortfolioAnalyticsEvent.EventBuilder,
val data: Flow<CryptoCurrencyData>,
val callbacks: Callbacks,
val bottomAction: BottomAction = BottomAction.Later,
val isRedesignForced: Boolean = false,
)
enum class BottomAction { Later, GoToToken }
interface Callbacks {
fun onLaterClick()
fun onBottomActionClick()
}
@AssistedFactory

View file

@ -307,7 +307,7 @@ internal class AddToPortfolioModel @Inject constructor(
.onEmpty { finishSuccessFlow(result) }
.launchIn(this)
callbackDelegate.onLaterClick.receiveAsFlow().first()
callbackDelegate.onChooseTokenBottomActionClick.receiveAsFlow().first()
analyticsEventHandler.send(eventBuilder.getTokenLater())
finishSuccessFlow(result)
}
@ -524,7 +524,7 @@ internal class AddToPortfolioCallbackDelegate @Inject constructor() :
UserPortfolioComponent.Callbacks {
val onNetworkSelected = Channel<TokenMarketInfo.Network>()
val onLaterClick = Channel<Unit>()
val onChooseTokenBottomActionClick = Channel<Unit>()
val onChangeNetworkClick = Channel<Unit>()
val onChangePortfolioClick = Channel<Unit>()
val onTokenAdded = Channel<CryptoCurrencyStatus>()
@ -534,8 +534,8 @@ internal class AddToPortfolioCallbackDelegate @Inject constructor() :
onNetworkSelected.trySend(network)
}
override fun onLaterClick() {
onLaterClick.trySend(Unit)
override fun onBottomActionClick() {
onChooseTokenBottomActionClick.trySend(Unit)
}
override fun onChangeNetworkClick() {

View file

@ -21,9 +21,12 @@ import com.tangem.core.ui.ds.badge.TangemBadgeShape
import com.tangem.core.ui.ds.badge.TangemBadgeSize
import com.tangem.core.ui.ds.badge.TangemBadgeUM
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.core.ui.res.TangemTheme
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
@ -48,7 +51,7 @@ internal class TokenActionsUiBuilder @Inject constructor(
appCurrency: AppCurrency,
isBalanceHidden: Boolean,
): TokenActionsUM {
return if (designFeatureToggles.isRedesignEnabled) {
return if (designFeatureToggles.isRedesignEnabled || params.isRedesignForced) {
buildV2(
cryptoCurrencyData = cryptoCurrencyData,
tokenActionsHandler = tokenActionsHandler,
@ -85,8 +88,9 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
isRedesignEnabled = false,
),
onLaterClick = {
params.callbacks.onLaterClick()
bottomActionText = bottomActionText(params.bottomAction),
onBottomActionClick = {
params.callbacks.onBottomActionClick()
},
)
}
@ -115,14 +119,22 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
isRedesignEnabled = true,
),
onLaterClick = {
params.callbacks.onLaterClick()
bottomActionText = bottomActionText(params.bottomAction),
onBottomActionClick = {
params.callbacks.onBottomActionClick()
},
isBalancesHidden = isBalanceHidden,
portfolioBadge = createPortfolioBadge(cryptoCurrencyData = cryptoCurrencyData),
)
}
private fun bottomActionText(action: TokenActionsComponent.BottomAction): TextReference {
return when (action) {
TokenActionsComponent.BottomAction.Later -> resourceReference(R.string.common_later)
TokenActionsComponent.BottomAction.GoToToken -> resourceReference(R.string.common_go_to_token)
}
}
private fun createPortfolioBadge(cryptoCurrencyData: CryptoCurrencyData): PortfolioBadgeUM {
return if (cryptoCurrencyData.isAccountMode) {
val icon = CryptoPortfolioIconConverter.convert(cryptoCurrencyData.account.account.icon)

View file

@ -33,7 +33,6 @@ import com.tangem.core.ui.components.token.TokenItem
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.LocalHapticManager
import com.tangem.core.ui.res.TangemColorPalette
@ -78,8 +77,8 @@ internal fun TokenActionsContent(state: TokenActionsUM, modifier: Modifier = Mod
SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = stringResourceSafe(R.string.common_later),
onClick = state.onLaterClick,
text = state.bottomActionText.resolveReference(),
onClick = state.onBottomActionClick,
)
}
}
@ -198,7 +197,8 @@ private class TokenActionsContentPreviewProvider : PreviewParameterProvider<Toke
onQuickActionLongClick = {},
),
token = tokenState,
onLaterClick = {},
bottomActionText = stringReference("Later"),
onBottomActionClick = {},
),
)
}

View file

@ -78,8 +78,8 @@ internal fun TokenActionsContentV2(state: TokenActionsUM, modifier: Modifier = M
CompositionLocalProvider(LocalHazeState provides rememberHazeState()) {
SecondaryTangemButton(
modifier = Modifier.fillMaxWidth(),
onClick = state.onLaterClick,
text = resourceReference(R.string.common_later),
onClick = state.onBottomActionClick,
text = state.bottomActionText,
size = TangemButtonSize.X12,
shape = TangemButtonShape.Rounded,
)
@ -234,7 +234,8 @@ private class TokenActionsContentPreviewProviderV2 : PreviewParameterProvider<To
onQuickActionLongClick = {},
),
token = tokenState,
onLaterClick = {},
bottomActionText = resourceReference(R.string.common_later),
onBottomActionClick = {},
portfolioBadge = PortfolioBadgeUM.Wallet(
name = stringReference("Wallet 2"),
deviceIcon = DeviceIconUM.Stub(cardsCount = 2),

View file

@ -10,7 +10,8 @@ import com.tangem.core.ui.extensions.TextReference
internal data class TokenActionsUM(
val token: TokenItemState,
val quickActions: QuickActions,
val onLaterClick: () -> Unit,
val bottomActionText: TextReference,
val onBottomActionClick: () -> Unit,
val isBalancesHidden: Boolean = false,
val portfolioBadge: PortfolioBadgeUM = PortfolioBadgeUM.None,
)

View file

@ -8,4 +8,6 @@ package com.tangem.features.wallet.featuretoggles
interface WalletFeatureToggles {
val isAddAndManageTokensEnabled: Boolean
val isAddFundsStage1Enabled: Boolean
}

View file

@ -63,6 +63,7 @@ import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import com.tangem.utils.logging.TangemLogger
@ -120,6 +121,7 @@ internal class WalletModel @Inject constructor(
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
private val uiMessageSender: UiMessageSender,
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
private val walletFeatureToggles: WalletFeatureToggles,
private val startAssetsDiscoveryUseCase: StartAssetsDiscoveryUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
@ -544,6 +546,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
),
)
@ -590,6 +593,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
),
)
}
@ -611,6 +615,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
),
)
}
@ -625,6 +630,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
),
)
@ -686,6 +692,7 @@ internal class WalletModel @Inject constructor(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled,
),
)

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.onramp.FetchHotCryptoUseCase
import com.tangem.domain.settings.NeverToShowWalletsScrollPreview
@ -114,6 +115,10 @@ internal class WalletClickIntents @Inject constructor(
refreshSingleCurrencyContent(showRefreshState = true)
}
fun onAddFundsClick(userWalletId: UserWalletId) {
router.openAddFunds(userWalletId)
}
private fun refreshMultiCurrencyContent(showRefreshState: Boolean) {
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return

View file

@ -11,4 +11,7 @@ internal class DefaultWalletFeatureToggles @Inject constructor(
override val isAddAndManageTokensEnabled: Boolean
get() = featureToggles.isFeatureEnabled(FeatureToggles.ADD_AND_MANAGE_TOKENS_ENABLED)
override val isAddFundsStage1Enabled: Boolean
get() = featureToggles.isFeatureEnabled(FeatureToggles.AND_15310_ADD_FUNDS_STAGE1)
}

View file

@ -119,6 +119,10 @@ internal class DefaultWalletRouter @Inject constructor(
router.push(AppRoute.Home())
}
override fun openAddFunds(userWalletId: UserWalletId) {
router.push(AppRoute.AddFunds(userWalletId = userWalletId))
}
override fun isWalletLastScreen(): Boolean {
return router.stack.lastOrNull() is AppRoute.Wallet
}

View file

@ -117,4 +117,7 @@ internal interface InnerWalletRouter {
/** Open network selection bottom sheet for multiple QR matches */
fun openNetworkSelectionBottomSheet(target: QrSendTarget.Multiple)
/** Open Add Funds screen */
fun openAddFunds(userWalletId: UserWalletId)
}

View file

@ -51,6 +51,20 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
),
)
data class AddFunds(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_add_funds),
iconResId = R.drawable.ic_plus_24,
onClick = onClick,
isEnabled = enabled,
shouldDimContent = dimContent,
),
)
/**
* Send
*

View file

@ -13,6 +13,7 @@ internal class AddWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -20,6 +21,7 @@ internal class AddWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
)
}

View file

@ -27,6 +27,7 @@ internal class InitializeWalletsTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -34,6 +35,7 @@ internal class InitializeWalletsTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
)
}
@ -147,8 +149,18 @@ internal class InitializeWalletsTransformer(
userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
if (isSingleWalletWithToken) return persistentListOf()
val firstButton = if (isAddFundsStage1Enabled) {
WalletManageButton.AddFunds(
enabled = false,
dimContent = false,
onClick = {},
)
} else {
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {})
}
return persistentListOf(
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}),
firstButton,
WalletManageButton.Swap(enabled = false, dimContent = false, onClick = {}),
WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}),
)

View file

@ -24,6 +24,7 @@ internal class ReinitializeNewWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -31,6 +32,7 @@ internal class ReinitializeNewWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
)
}

View file

@ -20,6 +20,7 @@ internal class ReinitializeWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) : WalletStateTransformer(userWalletId = userWallet.walletId) {
private val walletLoadingStateFactory by lazy {
@ -27,6 +28,7 @@ internal class ReinitializeWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
)
}

View file

@ -72,10 +72,11 @@ internal class SetRefreshStateTransformer(
private fun PersistentList<WalletManageButton>.toUpdatedState(): PersistentList<WalletManageButton> {
val isButtonsEnabled = !isRefreshing
return mutate {
it.mapNotNull { button ->
return mutate { items ->
items.mapNotNull { button ->
when (button) {
is WalletManageButton.Buy -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.AddFunds -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.Receive -> button

View file

@ -18,6 +18,7 @@ internal class UnlockWalletTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -25,6 +26,7 @@ internal class UnlockWalletTransformer(
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
getWalletIconUseCase = getWalletIconUseCase,
isAddFundsStage1Enabled = isAddFundsStage1Enabled,
)
}

View file

@ -43,6 +43,7 @@ private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolea
.map { action ->
when (action) {
is WalletManageButton.Buy -> action.copy(enabled = enabled)
is WalletManageButton.AddFunds -> action.copy(enabled = enabled)
is WalletManageButton.Sell -> action.copy(enabled = enabled)
is WalletManageButton.Swap -> action.copy(enabled = enabled)
else -> action

View file

@ -34,6 +34,7 @@ internal class WalletLoadingStateFactory(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isAddFundsStage1Enabled: Boolean,
) {
fun create(userWallet: UserWallet): WalletState {
@ -149,7 +150,13 @@ internal class WalletLoadingStateFactory(
userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
if (isSingleWalletWithToken) return persistentListOf()
return persistentListOf(
val firstButton = if (isAddFundsStage1Enabled) {
WalletManageButton.AddFunds(
enabled = true,
dimContent = false,
onClick = { clickIntents.onAddFundsClick(userWallet.walletId) },
)
} else {
WalletManageButton.Buy(
enabled = true,
dimContent = false,
@ -159,7 +166,11 @@ internal class WalletLoadingStateFactory(
WALLET_TYPE,
)
},
),
)
}
return persistentListOf(
firstButton,
WalletManageButton.Swap(
enabled = true,
dimContent = false,