Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-21 17:59:40 +05:00
commit a22e77ebe8
669 changed files with 27695 additions and 7905 deletions

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,97 @@
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 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(
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
}
}

View file

@ -0,0 +1,28 @@
package com.tangem.features.commonfeatures.impl.addfunds.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
internal sealed class AddFundsAnalyticsEvent(
event: String,
params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category = CATEGORY, event = event, params = params) {
class MethodScreenOpened(source: String) : AddFundsAnalyticsEvent(
event = "Method Screen Opened",
params = mapOf(AnalyticsParam.SOURCE to source),
)
class ButtonBuy : AddFundsAnalyticsEvent(event = "Button - Buy")
class ButtonSwap : AddFundsAnalyticsEvent(event = "Button - Swap")
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

@ -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,121 @@
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.common.ui.markets.action.TokenActionsBSContentUM
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.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.addfunds.analytics.AddFundsAnalyticsEvent
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,
private val analyticsEventHandler: AnalyticsEventHandler,
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)
analyticsEventHandler.send(
AddFundsAnalyticsEvent.MethodScreenOpened(source = AddFundsAnalyticsEvent.SOURCE_MAIN_SCREEN),
)
observeBridge()
}
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,
),
)
}
override fun onQuickActionClick(action: TokenActionsBSContentUM.Action) {
val event = when (action) {
TokenActionsBSContentUM.Action.Buy -> AddFundsAnalyticsEvent.ButtonBuy()
TokenActionsBSContentUM.Action.Exchange -> AddFundsAnalyticsEvent.ButtonSwap()
TokenActionsBSContentUM.Action.Receive -> AddFundsAnalyticsEvent.ButtonReceive()
else -> return
}
analyticsEventHandler.send(event)
}
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

@ -58,7 +58,6 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
tokenActionsComponentFactory.create(
context = child("tokenActionsComponent"),
params = TokenActionsComponent.Params(
eventBuilder = model.eventBuilder,
callbacks = model,
data = model.tokenActionsData,
),

View file

@ -9,6 +9,7 @@ import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.common.ui.markets.action.CryptoCurrencyData
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.factory.ComponentFactory
@ -17,7 +18,6 @@ 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.analytics.PortfolioAnalyticsEvent
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
@ -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,
@ -72,13 +72,17 @@ internal class TokenActionsComponent @AssistedInject constructor(
)
data class Params(
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()
fun onQuickActionClick(action: TokenActionsBSContentUM.Action) {}
}
@AssistedFactory

View file

@ -7,6 +7,7 @@ import com.arkivanov.decompose.router.stack.replaceAll
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.common.ui.markets.action.CryptoCurrencyData
import com.tangem.common.ui.markets.action.QuickActionsConverter.toQuickActions
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -106,6 +107,10 @@ 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,
@ -157,6 +162,7 @@ internal class AddToPortfolioModel @Inject constructor(
addToPortfolioManager.onSuccessAdded(result)
channel.close()
}
fun finishOnAddedTokenClick(result: AddToPortfolioManager.Result) {
addToPortfolioManager.onAddedTokenClick(result)
channel.close()
@ -306,7 +312,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)
}
@ -523,7 +529,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>()
@ -533,8 +539,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

@ -4,7 +4,6 @@ import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.ui.markets.action.TokenActionsBSContentUM
import com.tangem.common.ui.markets.action.TokenActionsHandler
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
@ -31,12 +30,10 @@ internal class TokenActionsModel @Inject constructor(
tokenActionsIntentsFactory: TokenActionsHandler.Factory,
override val dispatchers: CoroutineDispatcherProvider,
private val uiBuilder: TokenActionsUiBuilder,
private val analyticsEventHandler: AnalyticsEventHandler,
private val receiveAddressesFactory: ReceiveAddressesFactory,
) : Model() {
private val params = paramsContainer.require<TokenActionsComponent.Params>()
private val analyticsEventBuilder get() = params.eventBuilder
private val currentAppCurrency = getSelectedAppCurrencyUseCase.invokeOrDefault()
.stateIn(
scope = modelScope,
@ -68,6 +65,7 @@ internal class TokenActionsModel @Inject constructor(
isBalanceHidden = isBalanceHidden,
)
}
.flowOn(dispatchers.default)
.stateIn(
scope = modelScope,
started = SharingStarted.Eagerly,
@ -75,11 +73,10 @@ internal class TokenActionsModel @Inject constructor(
)
private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) = modelScope.launch {
val event = analyticsEventBuilder.getTokenActionClick(actionUM = handledAction.action)
analyticsEventHandler.send(event)
params.callbacks.onQuickActionClick(handledAction.action)
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
if (!isReceive) return@launch
modelScope.launch {
modelScope.launch(dispatchers.default) {
val tokenConfig = receiveAddressesFactory.create(
status = handledAction.cryptoCurrencyData.status,
userWalletId = handledAction.cryptoCurrencyData.userWallet.walletId,

View file

@ -3,27 +3,35 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.model
import androidx.compose.ui.text.SpanStyle
import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount
import com.tangem.common.ui.account.*
import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.getResId
import com.tangem.common.ui.account.getUiColor
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
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.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.R
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.ds.badge.TangemBadgeIconPosition
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
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 java.math.BigDecimal
import javax.inject.Inject
@ -32,6 +40,8 @@ import javax.inject.Inject
internal class TokenActionsUiBuilder @Inject constructor(
paramsContainer: ParamsContainer,
private val designFeatureToggles: DesignFeatureToggles,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletIconUMConverter: WalletIconUMConverter,
) {
private val params = paramsContainer.require<TokenActionsComponent.Params>()
@ -41,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,
@ -78,8 +88,9 @@ internal class TokenActionsUiBuilder @Inject constructor(
tokenActionsHandler = tokenActionsHandler,
isRedesignEnabled = false,
),
onLaterClick = {
params.callbacks.onLaterClick()
bottomActionText = bottomActionText(params.bottomAction),
onBottomActionClick = {
params.callbacks.onBottomActionClick()
},
)
}
@ -108,50 +119,53 @@ 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),
portfolioBadge = createPortfolioBadge(cryptoCurrencyData = cryptoCurrencyData),
)
}
private fun createPortfolioBadge(cryptoCurrencyData: CryptoCurrencyData): TangemBadgeUM {
val icon: AccountIconUM?
val name = if (cryptoCurrencyData.isAccountMode) {
icon = CryptoPortfolioIconConverter.convert(cryptoCurrencyData.account.account.icon)
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)
val name = cryptoCurrencyData
.account
.account
.accountName
.toUM()
.value
PortfolioBadgeUM.Account(
badge = TangemBadgeUM(
text = name,
tangemIconUM = TangemIconUM.Icon(
iconRes = icon.value.getResId(),
tintReference = { icon.color.getUiColor() },
),
size = TangemBadgeSize.X6,
shape = TangemBadgeShape.Rounded,
iconPosition = TangemBadgeIconPosition.Start,
shouldRespectIconTint = true,
),
)
} else {
icon = null
stringReference(cryptoCurrencyData.userWallet.name)
val userWallet = cryptoCurrencyData.userWallet
PortfolioBadgeUM.Wallet(
name = stringReference(userWallet.name),
deviceIcon = walletIconUMConverter.convert(
getWalletIconUseCase(cryptoCurrencyData.userWallet),
),
)
}
return TangemBadgeUM(
text = name,
tangemIconUM = if (icon == null) {
TangemIconUM.Icon(
iconRes = R.drawable.ic_key_card_20,
tintReference = { TangemTheme.colors2.graphic.neutral.tertiary },
)
} else {
TangemIconUM.Icon(
iconRes = icon.value.getResId(),
tintReference = { icon.color.getUiColor() },
)
},
size = TangemBadgeSize.X6,
shape = TangemBadgeShape.Rounded,
iconPosition = if (cryptoCurrencyData.isAccountMode) {
TangemBadgeIconPosition.Start
} else {
TangemBadgeIconPosition.End
},
shouldRespectIconTint = cryptoCurrencyData.isAccountMode,
)
}
private fun createSubtitle2State(status: CryptoCurrencyStatus): TokenItemState.Subtitle2State? {

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

@ -1,22 +1,16 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.ui
import android.content.res.Configuration
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -25,32 +19,30 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.common.ui.markets.action.QuickActionUM
import com.tangem.common.ui.markets.action.QuickActions
import com.tangem.common.ui.tokenaction.TokenActionRow
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.ds.badge.*
import com.tangem.core.ui.ds.badge.TangemBadge
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.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.ds.image.TangemDeviceIcon
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.formatStyled
import com.tangem.core.ui.format.bigdecimal.price
import com.tangem.core.ui.haptic.TangemHapticEffect
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 dev.chrisbanes.haze.rememberHazeState
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
import java.util.UUID
private const val ACTION_BACKGROUND_ALPHA = .1f
@Composable
internal fun TokenActionsContentV2(state: TokenActionsUM, modifier: Modifier = Modifier) {
Column(
@ -69,10 +61,13 @@ internal fun TokenActionsContentV2(state: TokenActionsUM, modifier: Modifier = M
) {
state.quickActions.actions.fastForEach { actionUM ->
key(actionUM.title) {
ActionRow(
state = actionUM,
TokenActionRow(
iconRes = actionUM.icon,
title = actionUM.title,
description = actionUM.description,
onClick = { state.quickActions.onQuickActionClick(actionUM) },
onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) },
onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) }
.takeIf { actionUM.isLongClickAvailable },
)
}
}
@ -83,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,
)
@ -92,81 +87,11 @@ internal fun TokenActionsContentV2(state: TokenActionsUM, modifier: Modifier = M
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ActionRow(
state: QuickActionUM,
onClick: () -> Unit,
onLongClick: (() -> Unit),
modifier: Modifier = Modifier,
) {
val hapticManager = LocalHapticManager.current
val onLongClickInternal = {
hapticManager.perform(TangemHapticEffect.View.LongPress)
onLongClick()
}
TangemRowContainer(
modifier = modifier
.combinedClickable(
onLongClick = onLongClickInternal.takeIf { state.isLongClickAvailable },
onClick = {
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
onClick()
},
)
.background(
color = TangemTheme.colors2.surface.level3,
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
),
) {
Box(
modifier = Modifier
.layoutId(TangemRowLayoutId.HEAD)
.padding(end = TangemTheme.dimens2.x3)
.size(40.dp)
.background(
color = TangemTheme.colors2.graphic.status.accent.copy(alpha = ACTION_BACKGROUND_ALPHA),
shape = CircleShape,
),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(20.dp),
imageVector = ImageVector.vectorResource(id = state.icon),
contentDescription = null,
tint = TangemTheme.colors2.graphic.status.accent,
)
}
Text(
modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP),
text = state.title.resolveReference(),
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
)
Text(
modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM),
text = state.description.resolveReference(),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
)
Icon(
modifier = Modifier
.layoutId(TangemRowLayoutId.TAIL)
.padding(start = TangemTheme.dimens2.x2)
.size(24.dp),
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
tint = TangemTheme.colors2.graphic.neutral.tertiary,
contentDescription = null,
)
}
}
@Composable
private fun TokenHeader(
addedToken: TokenItemState,
isBalanceHidden: Boolean,
portfolioBadge: TangemBadgeUM?,
portfolioBadge: PortfolioBadgeUM,
modifier: Modifier = Modifier,
) {
Column(
@ -209,8 +134,38 @@ private fun TokenHeader(
SpacerH(TangemTheme.dimens2.x7)
if (portfolioBadge == null) return
TangemBadge(portfolioBadge)
when (portfolioBadge) {
is PortfolioBadgeUM.None -> Unit
is PortfolioBadgeUM.Account -> TangemBadge(portfolioBadge.badge)
is PortfolioBadgeUM.Wallet -> WalletPortfolioRow(
name = portfolioBadge.name,
deviceIcon = portfolioBadge.deviceIcon,
)
}
}
}
@Composable
private fun WalletPortfolioRow(name: TextReference, deviceIcon: DeviceIconUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.heightIn(TangemTheme.dimens2.x6)
.clip(RoundedCornerShape(TangemTheme.dimens2.x4))
.background(TangemTheme.colors2.markers.backgroundSolidGray)
.padding(start = TangemTheme.dimens2.x3, end = TangemTheme.dimens2.x2),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
) {
Text(
text = name.resolveReference(),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.markers.textGray,
maxLines = 1,
)
TangemDeviceIcon(
state = deviceIcon,
modifier = Modifier.size(TangemTheme.dimens2.x4),
)
}
}
@ -279,16 +234,11 @@ private class TokenActionsContentPreviewProviderV2 : PreviewParameterProvider<To
onQuickActionLongClick = {},
),
token = tokenState,
onLaterClick = {},
portfolioBadge = TangemBadgeUM(
text = stringReference("Wallet 2"),
tangemIconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_key_card_20,
tintReference = { TangemTheme.colors2.graphic.neutral.tertiary },
),
size = TangemBadgeSize.X6,
shape = TangemBadgeShape.Rounded,
iconPosition = TangemBadgeIconPosition.End,
bottomActionText = resourceReference(R.string.common_later),
onBottomActionClick = {},
portfolioBadge = PortfolioBadgeUM.Wallet(
name = stringReference("Wallet 2"),
deviceIcon = DeviceIconUM.Stub(cardsCount = 2),
),
),
)

View file

@ -1,13 +1,24 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.markets.action.QuickActions
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.ds.badge.TangemBadgeUM
import com.tangem.core.ui.ds.image.DeviceIconUM
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: TangemBadgeUM? = null,
)
val portfolioBadge: PortfolioBadgeUM = PortfolioBadgeUM.None,
)
@Immutable
internal sealed interface PortfolioBadgeUM {
data class Account(val badge: TangemBadgeUM) : PortfolioBadgeUM
data class Wallet(val name: TextReference, val deviceIcon: DeviceIconUM) : PortfolioBadgeUM
data object None : PortfolioBadgeUM
}

View file

@ -1,8 +1,10 @@
package com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.state
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
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
@ -17,6 +19,8 @@ import kotlinx.coroutines.flow.*
internal class UserPortfolioStateController @AssistedInject constructor(
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletIconUMConverter: WalletIconUMConverter,
@Assisted private val modelScope: CoroutineScope,
@Assisted private val onTokenSelected: (AddToPortfolioManager.Result) -> Unit,
) {
@ -36,6 +40,9 @@ internal class UserPortfolioStateController @AssistedInject constructor(
rawCurrencyId = rawCurrencyId,
appCurrency = appCurrency,
isBalanceHidden = isBalanceHidden,
resolveWalletDeviceIcon = {
walletIconUMConverter.convert(getWalletIconUseCase(it))
},
onTokenSelected = onTokenSelected,
).transform()
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToI
import com.tangem.common.ui.markets.tokenselector.*
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
@ -31,6 +32,7 @@ internal class UserPortfolioSectionsTransformer(
private val rawCurrencyId: CryptoCurrency.RawID,
private val appCurrency: AppCurrency,
private val isBalanceHidden: Boolean,
private val resolveWalletDeviceIcon: (UserWallet) -> DeviceIconUM,
private val onTokenSelected: (AddToPortfolioManager.Result) -> Unit,
) {
@ -68,8 +70,12 @@ internal class UserPortfolioSectionsTransformer(
for ((_, walletEntries) in byWallet) {
if (shouldShowWalletHeaders) {
val wallet = walletEntries.first().wallet
sections.add(
TokenSelectorSectionUM.WalletHeader(walletName = walletEntries.first().wallet.name),
TokenSelectorSectionUM.WalletHeader(
walletName = wallet.name,
deviceIcon = resolveWalletDeviceIcon(wallet),
),
)
}

View file

@ -187,8 +187,8 @@ internal class ChooseTokenListItemConverter(
is PaymentAccountStatusValue.UnderReview,
PaymentAccountStatusValue.Loading,
PaymentAccountStatusValue.Empty,
is PaymentAccountStatusValue.Deactivated,
-> return null
is PaymentAccountStatusValue.Deactivated -> status.cryptoCurrencyStatus
is PaymentAccountStatusValue.Loaded -> status.cryptoCurrencyStatus
}
val account = this.account

View file

@ -2,10 +2,12 @@ package com.tangem.features.commonfeatures.impl.portfolioselector
import com.tangem.common.ui.account.AccountPortfolioItemUMConverter
import com.tangem.common.ui.userwallet.converter.UserWalletItemUMConverter
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
@ -15,6 +17,7 @@ import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.account.filterCryptoPortfolio
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
import com.tangem.features.commonfeatures.impl.R
@ -33,6 +36,8 @@ internal class PortfolioSelectorModel @Inject constructor(
paramsContainer: ParamsContainer,
walletImageFetcher: UserWalletImageFetcher,
isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletIconUMConverter: WalletIconUMConverter,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
@ -173,6 +178,7 @@ internal class PortfolioSelectorModel @Inject constructor(
val walletTitle = PortfolioSelectorItemUM.GroupTitle(
id = "GroupTitle ${wallet.walletId.stringValue}",
name = stringReference(wallet.name),
deviceIcon = walletIconUMConverter.convert(getWalletIconUseCase(wallet)),
)
add(walletTitle)
@ -204,6 +210,7 @@ internal class PortfolioSelectorModel @Inject constructor(
val lockedWalletsTitle = PortfolioSelectorItemUM.GroupTitle(
id = "lockedWalletsTitleId",
name = resourceReference(R.string.common_locked_wallets),
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
)
return listOf(lockedWalletsTitle) + wallets

View file

@ -2,6 +2,7 @@ package com.tangem.features.commonfeatures.impl.portfolioselector.entity
import androidx.compose.runtime.Immutable
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
@ -17,6 +18,7 @@ sealed interface PortfolioSelectorItemUM {
data class GroupTitle(
override val id: String,
val name: TextReference,
val deviceIcon: DeviceIconUM,
) : PortfolioSelectorItemUM
data class Portfolio(

View file

@ -11,9 +11,11 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
@ -26,10 +28,12 @@ import com.tangem.common.ui.account.AccountIconPreviewData
import com.tangem.common.ui.userwallet.UserWalletItemRow
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.LocalCanScrollBackward
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.commonfeatures.impl.R
@ -46,51 +50,59 @@ internal fun PortfolioSelectorContent(
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(),
) {
LazyColumn(
modifier = modifier,
contentPadding = contentPadding,
val lazyListState = rememberLazyListState()
CompositionLocalProvider(
LocalCanScrollBackward provides
lazyListState.canScrollBackward,
) {
val items = state.items
itemsIndexed(
items = items,
key = { _, item -> item.id },
) { index, item ->
val previewItem = items.getOrNull(index.dec())
val offsetModifier = when {
previewItem == null -> Modifier
item is PortfolioSelectorItemUM.GroupTitle -> Modifier.padding(
top = TangemTheme.dimens.spacing16,
)
else -> Modifier.padding(
top = TangemTheme.dimens.spacing8,
)
}
LazyColumn(
state = lazyListState,
modifier = modifier,
contentPadding = contentPadding,
) {
val items = state.items
itemsIndexed(
items = items,
key = { _, item -> item.id },
) { index, item ->
val previewItem = items.getOrNull(index.dec())
val offsetModifier = when {
previewItem == null -> Modifier
item is PortfolioSelectorItemUM.GroupTitle -> Modifier.padding(
top = TangemTheme.dimens.spacing16,
)
else -> Modifier.padding(
top = TangemTheme.dimens.spacing8,
)
}
val portfolioShape = RoundedCornerShape(TangemTheme.dimens.radius14)
val border = BorderStroke(
width = 1.dp,
color = TangemTheme.colors.text.accent,
)
val portfolioShape = RoundedCornerShape(TangemTheme.dimens.radius14)
val border = BorderStroke(
width = 1.dp,
color = TangemTheme.colors.text.accent,
)
when (item) {
is PortfolioSelectorItemUM.Portfolio -> UserWalletItemRow(
state = item.item,
modifier = offsetModifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size68)
.clip(portfolioShape)
.background(TangemTheme.colors.background.action)
.conditional(item.isSelected) { border(border, portfolioShape) }
.clickable(enabled = item.item.isEnabled, onClick = item.item.onClick)
.padding(all = TangemTheme.dimens.spacing12)
.conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) },
)
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
model = item,
modifier = offsetModifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing16),
)
when (item) {
is PortfolioSelectorItemUM.Portfolio -> UserWalletItemRow(
state = item.item,
modifier = offsetModifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size68)
.clip(portfolioShape)
.background(TangemTheme.colors.background.action)
.conditional(item.isSelected) { border(border, portfolioShape) }
.clickable(enabled = item.item.isEnabled, onClick = item.item.onClick)
.padding(all = TangemTheme.dimens.spacing12)
.conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) },
)
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
model = item,
modifier = offsetModifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing16),
)
}
}
}
}
@ -161,12 +173,14 @@ internal object PortfolioSelectorPreviewData {
PortfolioSelectorItemUM.GroupTitle(
id = UUID.randomUUID().toString(),
name = stringReference("Tangem 2.0"),
deviceIcon = DeviceIconUM.Stub(cardsCount = 2),
),
PortfolioSelectorItemUM.Portfolio(accountItem, false),
PortfolioSelectorItemUM.Portfolio(lockedAccountItem, false),
PortfolioSelectorItemUM.GroupTitle(
id = UUID.randomUUID().toString(),
name = stringReference("Tangem White"),
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
),
PortfolioSelectorItemUM.Portfolio(accountItem, true),
)
@ -176,6 +190,7 @@ internal object PortfolioSelectorPreviewData {
PortfolioSelectorItemUM.GroupTitle(
id = UUID.randomUUID().toString(),
name = resourceReference(R.string.common_locked_wallets),
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
),
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
@ -193,6 +208,7 @@ internal object PortfolioSelectorPreviewData {
PortfolioSelectorItemUM.GroupTitle(
id = UUID.randomUUID().toString(),
name = resourceReference(R.string.common_locked_wallets),
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
),
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),

View file

@ -7,16 +7,14 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Icon
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -25,14 +23,15 @@ import com.tangem.common.ui.userwallet.CardImage
import com.tangem.common.ui.userwallet.getBalanceValueAndFlickerState
import com.tangem.common.ui.userwallet.getInformationValue
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.R
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.text.applyBladeBrush
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.ds.image.TangemDeviceIcon
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.LocalCanScrollBackward
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
@ -48,20 +47,41 @@ internal fun PortfolioSelectorContentV2(
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(),
) {
LazyColumn(
modifier = modifier,
contentPadding = contentPadding,
val lazyListState = rememberLazyListState()
CompositionLocalProvider(
LocalCanScrollBackward provides
lazyListState.canScrollBackward,
) {
val items = state.items
itemsIndexed(
items = items,
key = { _, item -> item.id },
) { index, item ->
when (item) {
is PortfolioSelectorItemUM.Portfolio ->
PortfolioSelectorItem(
state = item.item,
LazyColumn(
state = lazyListState,
modifier = modifier,
contentPadding = contentPadding,
) {
val items = state.items
itemsIndexed(
items = items,
key = { _, item -> item.id },
) { index, item ->
when (item) {
is PortfolioSelectorItemUM.Portfolio ->
PortfolioSelectorItem(
state = item.item,
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.items.lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3,
radius = TangemTheme.dimens2.x5,
addDefaultPadding = false,
)
.clickable(enabled = item.item.isEnabled, onClick = item.item.onClick)
.conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) },
)
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
model = item,
modifier = Modifier
.fillMaxWidth()
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.items.lastIndex,
@ -69,22 +89,9 @@ internal fun PortfolioSelectorContentV2(
radius = TangemTheme.dimens2.x5,
addDefaultPadding = false,
)
.clickable(enabled = item.item.isEnabled, onClick = item.item.onClick)
.conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) },
.padding(horizontal = TangemTheme.dimens.spacing16),
)
is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow(
model = item,
modifier = Modifier
.fillMaxWidth()
.roundedShapeItemDecoration(
currentIndex = index,
lastIndex = state.items.lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3,
radius = TangemTheme.dimens2.x5,
addDefaultPadding = false,
)
.padding(horizontal = TangemTheme.dimens.spacing16),
)
}
}
}
}
@ -197,13 +204,11 @@ private fun WalletNameRow(model: PortfolioSelectorItemUM.GroupTitle, modifier: M
overflow = TextOverflow.Ellipsis,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_key_card_20),
TangemDeviceIcon(
state = model.deviceIcon,
modifier = Modifier
.align(Alignment.Bottom)
.size(TangemTheme.dimens2.x5),
tint = TangemTheme.colors2.graphic.neutral.tertiary,
contentDescription = null,
)
}
}