Updated on 2026-08-14
This commit is contained in:
commit
06a915a0f4
2186 changed files with 101417 additions and 36593 deletions
|
|
@ -11,16 +11,22 @@ android {
|
|||
|
||||
dependencies {
|
||||
|
||||
// region Kotlin
|
||||
api(deps.kotlin.coroutines)
|
||||
api(deps.kotlin.immutable.collections)
|
||||
api(deps.kotlin.serialization.core)
|
||||
// endregion
|
||||
|
||||
/* Project - Domain */
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.markets)
|
||||
implementation(projects.domain.account)
|
||||
api(projects.domain.account)
|
||||
api(projects.domain.appCurrency.models)
|
||||
api(projects.domain.markets.models)
|
||||
api(projects.domain.models)
|
||||
|
||||
/* Project - Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
api(projects.core.decompose)
|
||||
api(projects.core.ui)
|
||||
|
||||
/* Compose */
|
||||
implementation(deps.compose.runtime)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* How to use
|
||||
|
|
@ -32,6 +33,16 @@ interface PortfolioSelectorComponent : ComposableBottomSheetComponent, Composabl
|
|||
val portfolioFetcher: PortfolioFetcher,
|
||||
val controller: PortfolioSelectorController,
|
||||
val bsCallback: BottomSheetCallback? = null,
|
||||
val settings: Settings = Settings(),
|
||||
)
|
||||
|
||||
/**
|
||||
* @param isWalletSelectionOnly when `true`, the selector always shows a flat wallet list and ignores the global
|
||||
* accounts mode (no account grouping).
|
||||
*/
|
||||
data class Settings(
|
||||
val isWalletSelectionOnly: Boolean = false,
|
||||
val isMultiChoice: Boolean = false,
|
||||
)
|
||||
|
||||
interface BottomSheetCallback {
|
||||
|
|
@ -49,8 +60,12 @@ interface PortfolioSelectorComponent : ComposableBottomSheetComponent, Composabl
|
|||
*/
|
||||
interface PortfolioSelectorController {
|
||||
val isAccountMode: Flow<Boolean>
|
||||
val selectedAccount: Flow<AccountId?>
|
||||
val selectedAccountSync: AccountId?
|
||||
|
||||
val selectedAccounts: Flow<Set<AccountId>>
|
||||
val selectedAccount: Flow<AccountId?> get() = selectedAccounts.map { it.firstOrNull() }
|
||||
|
||||
val selectedAccountsSync: Set<AccountId>
|
||||
val selectedAccountSync: AccountId? get() = selectedAccountsSync.firstOrNull()
|
||||
|
||||
/**
|
||||
* for some Feature specific filtering
|
||||
|
|
@ -59,8 +74,15 @@ interface PortfolioSelectorController {
|
|||
val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean>
|
||||
suspend fun isAccountModeSync(): Boolean
|
||||
|
||||
fun selectAccount(accountId: AccountId?)
|
||||
fun selectAccount(accountIds: Set<AccountId>)
|
||||
fun selectAccount(accountId: AccountId?) = selectAccount(accountId?.let { setOf(it) }.orEmpty())
|
||||
|
||||
fun selectedAccountsWithData(
|
||||
portfolioFetcher: PortfolioFetcher,
|
||||
): Flow<Set<Pair<UserWallet, AccountStatus.CryptoPortfolio>>>
|
||||
|
||||
fun selectedAccountWithData(
|
||||
portfolioFetcher: PortfolioFetcher,
|
||||
): Flow<Pair<UserWallet, AccountStatus.CryptoPortfolio>?>
|
||||
): Flow<Pair<UserWallet, AccountStatus.CryptoPortfolio>?> =
|
||||
selectedAccountsWithData(portfolioFetcher).map { it.firstOrNull() }
|
||||
}
|
||||
|
|
@ -12,70 +12,70 @@ android {
|
|||
}
|
||||
dependencies {
|
||||
/** Api */
|
||||
implementation(projects.features.commonFeatures.api)
|
||||
implementation(projects.features.wallet.api)
|
||||
implementation(projects.features.tokenRecieve.api)
|
||||
api(projects.features.commonFeatures.api)
|
||||
api(projects.features.tokenRecieve.api)
|
||||
api(projects.features.wallet.api)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.analytics)
|
||||
api(projects.core.analytics)
|
||||
api(projects.core.decompose)
|
||||
api(projects.core.ui)
|
||||
api(projects.core.utils)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.core.ui)
|
||||
implementation(projects.core.error)
|
||||
implementation(projects.core.res)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.pagination)
|
||||
|
||||
/** Domain */
|
||||
implementation(projects.domain.models)
|
||||
api(projects.domain.account.status)
|
||||
api(projects.domain.appCurrency)
|
||||
api(projects.domain.appCurrency.models)
|
||||
api(projects.domain.balanceHiding)
|
||||
api(projects.domain.manageTokens)
|
||||
api(projects.domain.markets)
|
||||
api(projects.domain.markets.models)
|
||||
api(projects.domain.models)
|
||||
api(projects.domain.transaction)
|
||||
api(projects.domain.wallets)
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.account.status)
|
||||
implementation(projects.domain.card)
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.appCurrency)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.markets)
|
||||
implementation(projects.domain.transaction)
|
||||
implementation(projects.domain.tokens)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.manageTokens)
|
||||
implementation(projects.domain.manageTokens.models)
|
||||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
|
||||
/** Tangem libraries */
|
||||
implementation(tangemDeps.card.core)
|
||||
implementation(tangemDeps.blockchain)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common.ui)
|
||||
api(projects.common.routing)
|
||||
api(projects.common.ui)
|
||||
api(projects.common.uiMarkets)
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.uiCharts)
|
||||
implementation(projects.common.uiMarkets)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/** Libs */
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
api(projects.libs.blockchainSdk)
|
||||
implementation(projects.libs.crypto)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
implementation(deps.androidx.appCompat)
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
|
||||
/** Compose libraries */
|
||||
api(deps.compose.animation)
|
||||
api(deps.compose.foundation)
|
||||
implementation(deps.compose.material3)
|
||||
implementation(deps.compose.animation)
|
||||
implementation(deps.compose.foundation)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.decompose.ext.compose)
|
||||
implementation(deps.androidx.activity.compose)
|
||||
|
||||
/** Other libraries */
|
||||
api(deps.kotlin.coroutines)
|
||||
api(deps.kotlin.immutable.collections)
|
||||
implementation(deps.arrow.core)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.haze)
|
||||
implementation(deps.kotlin.serialization)
|
||||
implementation(deps.lifecycle.compose)
|
||||
implementation(deps.firebase.crashlytics)
|
||||
|
||||
/** DI */
|
||||
|
|
@ -84,5 +84,4 @@ dependencies {
|
|||
|
||||
testImplementation(projects.common.test)
|
||||
testImplementation(projects.test.core)
|
||||
testImplementation(projects.test.mock)
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.State
|
||||
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.userportfolio.model.UserPortfolioUM
|
||||
|
||||
|
|
@ -16,19 +15,11 @@ internal fun AddToPortfolioBottomSheetSwitch(
|
|||
userPortfolioState: State<UserPortfolioUM?>? = null,
|
||||
onAddFromUserPortfolioClick: (() -> Unit)? = null,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
AddToPortfolioBottomSheetV2(
|
||||
childStack = childStack,
|
||||
onBack = onBack,
|
||||
onDismiss = onDismiss,
|
||||
userPortfolioState = userPortfolioState,
|
||||
onAddFromUserPortfolioClick = onAddFromUserPortfolioClick,
|
||||
)
|
||||
} else {
|
||||
AddToPortfolioBottomSheet(
|
||||
childStack = childStack,
|
||||
onBack = onBack,
|
||||
onDismiss = onDismiss,
|
||||
)
|
||||
}
|
||||
AddToPortfolioBottomSheetV2(
|
||||
childStack = childStack,
|
||||
onBack = onBack,
|
||||
onDismiss = onDismiss,
|
||||
userPortfolioState = userPortfolioState,
|
||||
onAddFromUserPortfolioClick = onAddFromUserPortfolioClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -3,13 +3,11 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.addtoken.AddTokenContent
|
||||
import com.tangem.common.ui.addtoken.AddTokenContentV2
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedNetwork
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedPortfolio
|
||||
|
|
@ -31,17 +29,10 @@ internal class AddTokenComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val state = model.uiState.collectAsStateWithLifecycle()
|
||||
val um = state.value ?: return
|
||||
if (LocalRedesignEnabled.current) {
|
||||
AddTokenContentV2(
|
||||
modifier = modifier,
|
||||
state = um,
|
||||
)
|
||||
} else {
|
||||
AddTokenContent(
|
||||
modifier = modifier,
|
||||
state = um,
|
||||
)
|
||||
}
|
||||
AddTokenContentV2(
|
||||
modifier = modifier,
|
||||
state = um,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ 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.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
|
|
@ -56,7 +55,6 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val callbackDelegate: AddToPortfolioCallbackDelegate,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
|
||||
private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency,
|
||||
|
|
@ -182,9 +180,9 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
|
||||
setupPortfolioSelector(initialData, launchMode)
|
||||
|
||||
val shouldShowUserPortfolio = designFeatureToggles.isRedesignEnabled &&
|
||||
val shouldShowUserPortfolio =
|
||||
launchMode is AddToPortfolioManager.LaunchMode.ViaUserPortfolio &&
|
||||
initialData.hasAnyAddedCurrency(tokenMarketParams.id)
|
||||
initialData.hasAnyAddedCurrency(tokenMarketParams.id)
|
||||
|
||||
if (shouldShowUserPortfolio) {
|
||||
// suspend, must prepare UM before navigate to UserPortfolio
|
||||
|
|
@ -200,11 +198,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
AddToPortfolioManager.LaunchMode.Preselected -> null
|
||||
is AddToPortfolioManager.LaunchMode.ViaUserPortfolio,
|
||||
AddToPortfolioManager.LaunchMode.DirectAdd,
|
||||
-> if (designFeatureToggles.isRedesignEnabled) {
|
||||
getInitialSelection(initialData)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
-> getInitialSelection(initialData)
|
||||
}
|
||||
|
||||
val firstSelectedPortfolioFlow: Flow<SelectedPortfolio> =
|
||||
|
|
@ -272,15 +266,11 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
callbackDelegate.onChangePortfolioClick.receiveAsFlow()
|
||||
.onEach {
|
||||
middleNavigationJob?.cancel()
|
||||
middleNavigationJob = if (designFeatureToggles.isRedesignEnabled) {
|
||||
changePortfolioNavigationNewFlow(
|
||||
data = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
tokenParams = tokenMarketParams,
|
||||
)
|
||||
} else {
|
||||
changePortfolioNavigationFlow(initialData)
|
||||
}.launchIn(this)
|
||||
middleNavigationJob = changePortfolioNavigationNewFlow(
|
||||
data = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
tokenParams = tokenMarketParams,
|
||||
).launchIn(this)
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.pushNew(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
|
|
@ -369,33 +359,6 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changePortfolioNavigationFlow(data: AvailableToAddData): Flow<Unit> = flow {
|
||||
val selectedPortfolioValue = selectedPortfolio.first()
|
||||
val selectedAccount = selectedPortfolioValue.account.account.account.accountId
|
||||
portfolioSelectorController.selectAccount(selectedAccount)
|
||||
val changedPortfolio = setupPortfolioFlow(data)
|
||||
.drop(1)
|
||||
.onEach { portfolio ->
|
||||
val isSingleAvailableNetwork = portfolio.account.isSingleNetwork
|
||||
if (isSingleAvailableNetwork) {
|
||||
val singleNetwork = portfolio.account.availableToAddNetworks.first()
|
||||
callbackDelegate.onNetworkSelected.send(singleNetwork)
|
||||
} else {
|
||||
navigation.pushNew(routeToNetworkSelector(portfolio))
|
||||
}
|
||||
}
|
||||
val changedNetwork = setupNetworkFlow(changedPortfolio)
|
||||
combine(
|
||||
flow = changedPortfolio,
|
||||
flow2 = changedNetwork,
|
||||
transform = { newPortfolio, newNetwork ->
|
||||
selectedPortfolio.tryEmit(newPortfolio)
|
||||
selectedNetwork.tryEmit(newNetwork)
|
||||
navigation.popToFirst()
|
||||
},
|
||||
).collect { emit(it) }
|
||||
}
|
||||
|
||||
private fun changePortfolioNavigationNewFlow(
|
||||
data: AvailableToAddData,
|
||||
orderedNetworks: List<TokenMarketInfo.Network>,
|
||||
|
|
@ -432,7 +395,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
currency = addedToken.currency,
|
||||
accountId = selectedPortfolio.account.account.account.accountId,
|
||||
).onEach { state ->
|
||||
val requestedQuickActions = toQuickActions(state.states, designFeatureToggles.isRedesignEnabled)
|
||||
val requestedQuickActions = toQuickActions(state.states)
|
||||
when {
|
||||
requestedQuickActions.isNotEmpty() -> {
|
||||
timerJob.cancel()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ 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
|
||||
|
|
@ -36,7 +35,6 @@ import com.tangem.core.ui.ds.row.TangemRowContainer
|
|||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
|
|
@ -49,17 +47,10 @@ private const val DISABLED_ALPHA = 0.4f
|
|||
|
||||
@Composable
|
||||
internal fun ChooseNetworkContent(state: ChooseNetworkUM, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ChooseNetworkContentV2(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
ChooseNetworkContentV1(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ChooseNetworkContentV2(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -227,11 +218,9 @@ private fun Preview(@PreviewParameter(ChooseNetworkContentProvider::class) conte
|
|||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun PreviewV2(@PreviewParameter(ChooseNetworkContentProvider::class) content: ChooseNetworkUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
ChooseNetworkContent(
|
||||
state = content,
|
||||
)
|
||||
}
|
||||
ChooseNetworkContent(
|
||||
state = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ internal class ChooseTokenListItemConverter(
|
|||
val paymentCurrency: CryptoCurrencyStatus = when (val status = this.value) {
|
||||
is PaymentAccountStatusValue.Error,
|
||||
is PaymentAccountStatusValue.IssuingCard,
|
||||
is PaymentAccountStatusValue.AwaitingPlanSelection,
|
||||
is PaymentAccountStatusValue.Inactive,
|
||||
PaymentAccountStatusValue.NotCreated,
|
||||
is PaymentAccountStatusValue.UnderReview,
|
||||
PaymentAccountStatusValue.Loading,
|
||||
|
|
|
|||
|
|
@ -75,10 +75,9 @@ import com.tangem.core.ui.ds.row.token.TangemTokenRow
|
|||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.core.ui.test.BuyTokenScreenTestTags
|
||||
import com.tangem.core.ui.utils.ProvideSharedTransitionScope
|
||||
import com.tangem.core.ui.utils.TangemSharedTransitionLayout
|
||||
|
|
@ -136,13 +135,7 @@ private val ChooseTokenFullUM.isEmptyState: Boolean
|
|||
internal fun ChooseTokenScreen(state: ChooseTokenFullUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = if (LocalRedesignEnabled.current) {
|
||||
TangemTheme.colors2.surface.level2
|
||||
} else {
|
||||
TangemTheme.colors.background.secondary
|
||||
},
|
||||
)
|
||||
.background(color = TangemTheme.colors2.surface.level2)
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
|
|
@ -807,13 +800,7 @@ private fun LazyListScope.emptyTokensList(modifier: Modifier = Modifier) {
|
|||
item("EmptyTokensList") {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = if (LocalRedesignEnabled.current) {
|
||||
TangemTheme.colors2.surface.level2
|
||||
} else {
|
||||
TangemTheme.colors.background.secondary
|
||||
},
|
||||
)
|
||||
.background(color = TangemTheme.colors2.surface.level2)
|
||||
.fillParentMaxSize(),
|
||||
) {
|
||||
Column(modifier = Modifier.align(Alignment.Center)) {
|
||||
|
|
@ -844,13 +831,7 @@ private fun LazyListScope.tokensNotFound(modifier: Modifier = Modifier) {
|
|||
item("TokensNotFound") {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = if (LocalRedesignEnabled.current) {
|
||||
TangemTheme.colors2.surface.level2
|
||||
} else {
|
||||
TangemTheme.colors.background.secondary
|
||||
},
|
||||
)
|
||||
.background(color = TangemTheme.colors2.surface.level2)
|
||||
.fillParentMaxSize(),
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -870,7 +851,7 @@ private fun LazyListScope.tokensNotFound(modifier: Modifier = Modifier) {
|
|||
@Preview
|
||||
@Composable
|
||||
private fun TokenScreenPreview(@PreviewParameter(ChooseTokenScreenPreviewProvider::class) state: ChooseTokenFullUM) {
|
||||
TangemThemePreview {
|
||||
TangemThemePreviewRedesign {
|
||||
ChooseTokenScreen(
|
||||
state = state,
|
||||
modifier = Modifier,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.features.commonfeatures.impl.managefunds
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -11,6 +14,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.markets.action.TokenActionsContext
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -22,14 +26,13 @@ import com.tangem.core.ui.ds2.button.TangemButton
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||
import com.tangem.core.ui.test.BaseBottomSheetTestTags
|
||||
import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent
|
||||
import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent
|
||||
import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
import com.tangem.features.commonfeatures.impl.managefunds.model.ManageFundsModel
|
||||
import com.tangem.features.commonfeatures.impl.managefunds.model.uiSpec
|
||||
import com.tangem.common.ui.markets.action.TokenActionsContext
|
||||
import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent
|
||||
import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -140,6 +143,8 @@ internal class DefaultManageFundsComponent @AssistedInject constructor(
|
|||
AnimatedContent(
|
||||
targetState = route,
|
||||
modifier = animatedContentModifier,
|
||||
transitionSpec = { fadeIn().togetherWith(fadeOut()) },
|
||||
contentKey = { route -> route::class },
|
||||
label = "ManageFundsContentAnimation",
|
||||
) { animatedRoute ->
|
||||
ManageFundsRouteContent(
|
||||
|
|
|
|||
|
|
@ -9,10 +9,8 @@ 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.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorBS
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorContent
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorContentV2
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -48,19 +46,11 @@ internal class DefaultPortfolioSelectorComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val listBottomPadding = PaddingValues(bottom = LocalTangemBottomSheetContentBottomInset.current)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
PortfolioSelectorContentV2(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
contentPadding = listBottomPadding,
|
||||
)
|
||||
} else {
|
||||
PortfolioSelectorContent(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
contentPadding = listBottomPadding,
|
||||
)
|
||||
}
|
||||
PortfolioSelectorContentV2(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
contentPadding = listBottomPadding,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
|
|||
|
|
@ -18,38 +18,38 @@ internal class DefaultPortfolioSelectorController @Inject constructor(
|
|||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) : PortfolioSelectorController {
|
||||
|
||||
private val _selectedAccount: MutableSharedFlow<AccountId?> = MutableSharedFlow(
|
||||
private val _selectedAccount: MutableSharedFlow<Set<AccountId>> = MutableSharedFlow(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
override val isAccountMode: Flow<Boolean> by lazy { isAccountsModeEnabledUseCase() }
|
||||
// without StateFlow and distinctUntilChanged to allow reselect and correct navigation
|
||||
override val selectedAccount: Flow<AccountId?> get() = _selectedAccount
|
||||
override val selectedAccountSync: AccountId? get() = _selectedAccount.replayCache.firstOrNull()
|
||||
override val selectedAccounts: Flow<Set<AccountId>> get() = _selectedAccount
|
||||
override val selectedAccountsSync: Set<AccountId> get() = _selectedAccount.replayCache.firstOrNull().orEmpty()
|
||||
|
||||
override val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean> = MutableStateFlow { _, _ -> true }
|
||||
|
||||
override suspend fun isAccountModeSync(): Boolean = isAccountsModeEnabledUseCase.invokeSync()
|
||||
|
||||
override fun selectAccount(accountId: AccountId?) {
|
||||
_selectedAccount.tryEmit(accountId)
|
||||
override fun selectAccount(accountIds: Set<AccountId>) {
|
||||
_selectedAccount.tryEmit(accountIds)
|
||||
}
|
||||
|
||||
override fun selectedAccountWithData(
|
||||
override fun selectedAccountsWithData(
|
||||
portfolioFetcher: PortfolioFetcher,
|
||||
): Flow<Pair<UserWallet, AccountStatus.CryptoPortfolio>?> = combine(
|
||||
): Flow<Set<Pair<UserWallet, AccountStatus.CryptoPortfolio>>> = combine(
|
||||
flow = _selectedAccount,
|
||||
flow2 = portfolioFetcher.data,
|
||||
transform = { accountId, data ->
|
||||
accountId ?: return@combine null
|
||||
var result: Pair<UserWallet, AccountStatus.CryptoPortfolio>? = null
|
||||
transform = { accountIds, data ->
|
||||
val result = mutableSetOf<Pair<UserWallet, AccountStatus.CryptoPortfolio>>()
|
||||
|
||||
data.balances.forEach { wallet, balance ->
|
||||
val accountStatuses = balance.accountsBalance.accountStatuses
|
||||
data.balances.forEach { (_, balance) ->
|
||||
val accounts = balance.accountsBalance.accountStatuses
|
||||
.filterCryptoPortfolio()
|
||||
.find { accountId == it.account.accountId }
|
||||
if (accountStatuses != null) result = balance.userWallet to accountStatuses
|
||||
.filterTo(mutableSetOf()) { it.account.accountId in accountIds }
|
||||
.map { balance.userWallet to it }
|
||||
result.addAll(accounts)
|
||||
}
|
||||
|
||||
return@combine result
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorButtonUM
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorItemUM
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorUM
|
||||
import com.tangem.features.wallet.utils.UserWalletImageFetcher
|
||||
|
|
@ -48,31 +49,36 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
internal val state: StateFlow<PortfolioSelectorUM>
|
||||
field = MutableStateFlow<PortfolioSelectorUM>(emptyState())
|
||||
|
||||
init {
|
||||
val selectedAccountState = selectorController.selectedAccount
|
||||
.stateIn(modelScope, started = SharingStarted.Eagerly, initialValue = null)
|
||||
private val innerSelectedAccounts = MutableStateFlow(selectorController.selectedAccountsSync)
|
||||
|
||||
init {
|
||||
combine(
|
||||
flow = isAccountsModeEnabledUseCase(),
|
||||
flow2 = balanceFetcher.data,
|
||||
flow3 = walletImageFetcher.allWallets(ArtworkSize.SMALL),
|
||||
flow4 = selectorController.isEnabled,
|
||||
flow5 = selectedAccountState,
|
||||
flow5 = innerSelectedAccounts,
|
||||
transform = { isAccountsMode, portfolioData, artworks, isEnabled, selectedAccount ->
|
||||
val isAccountsModeEffective = isAccountsMode && !params.settings.isWalletSelectionOnly
|
||||
val uiList = buildUiList(
|
||||
isAccountsMode = isAccountsMode,
|
||||
isAccountsMode = isAccountsModeEffective,
|
||||
portfolioData = portfolioData,
|
||||
artworks = artworks,
|
||||
isEnabled = isEnabled,
|
||||
selectedAccount = selectedAccount,
|
||||
)
|
||||
val title = when (isAccountsMode) {
|
||||
val title = when (isAccountsModeEffective) {
|
||||
true -> resourceReference(R.string.common_choose_account)
|
||||
false -> resourceReference(R.string.common_choose_wallet)
|
||||
}
|
||||
val button = PortfolioSelectorButtonUM(
|
||||
text = resourceReference(R.string.common_apply),
|
||||
onClick = { onApplyClick() },
|
||||
)
|
||||
state.value = PortfolioSelectorUM(
|
||||
title = title,
|
||||
items = uiList.toImmutableList(),
|
||||
button = button.takeIf { params.settings.isMultiChoice },
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -85,7 +91,7 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
selectedAccount: AccountId?,
|
||||
selectedAccount: Set<AccountId>,
|
||||
): List<PortfolioSelectorItemUM> = when (isAccountsMode) {
|
||||
true -> buildAccountsList(
|
||||
portfolioData = portfolioData,
|
||||
|
|
@ -105,7 +111,7 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
selectedAccount: AccountId?,
|
||||
selectedAccount: Set<AccountId>,
|
||||
): List<PortfolioSelectorItemUM> = buildList {
|
||||
val appCurrency = portfolioData.appCurrency
|
||||
val isBalanceHidden = portfolioData.isBalanceHidden
|
||||
|
|
@ -113,25 +119,29 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
portfolioData.balances.forEach { (_, portfolio) ->
|
||||
val balance = portfolio.walletBalance
|
||||
val wallet = portfolio.userWallet
|
||||
val mainAccount = portfolio.accountsBalance.mainAccount
|
||||
val isSelected = mainAccount.accountId in selectedAccount
|
||||
val endIcon = if (isSelected) {
|
||||
UserWalletItemUM.EndIcon.Checkmark
|
||||
} else {
|
||||
UserWalletItemUM.EndIcon.None
|
||||
}
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
selectorController.selectAccount(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
},
|
||||
onClick = { onWalletSelected(wallet.walletId, portfolioData, false) },
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
artwork = artworks[wallet.walletId],
|
||||
isAuthMode = false,
|
||||
endIcon = endIcon,
|
||||
mode = UserWalletItemUMConverter.InfoField.Tokens(
|
||||
tokensCount = portfolio.accountsBalance.flattenCurrencies().size,
|
||||
),
|
||||
).convert(wallet)
|
||||
if (walletItemUM.isEnabled) {
|
||||
val mainAccount = portfolio.accountsBalance.mainAccount
|
||||
val isEnabledByFeature = isEnabled(wallet, mainAccount)
|
||||
val finalWalletItemUM =
|
||||
if (isEnabledByFeature) walletItemUM else walletItemUM.copy(isEnabled = false)
|
||||
val isSelected = mainAccount.isSelected(selectedAccount)
|
||||
add(PortfolioSelectorItemUM.Portfolio(finalWalletItemUM, isSelected))
|
||||
} else {
|
||||
lockedWallets.add(PortfolioSelectorItemUM.Portfolio(walletItemUM, isSelected = false))
|
||||
|
|
@ -147,7 +157,7 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
portfolioData: PortfolioFetcher.Data,
|
||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||
isEnabled: (UserWallet, AccountStatus) -> Boolean,
|
||||
selectedAccount: AccountId?,
|
||||
selectedAccount: Set<AccountId>,
|
||||
): List<PortfolioSelectorItemUM> = buildList {
|
||||
val appCurrency = portfolioData.appCurrency
|
||||
val isBalanceHidden = portfolioData.isBalanceHidden
|
||||
|
|
@ -157,7 +167,7 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
val wallet = portfolio.userWallet
|
||||
val walletItemUM = UserWalletItemUMConverter(
|
||||
onClick = {
|
||||
selectorController.selectAccount(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
onAccountSelected(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
balance = balance,
|
||||
|
|
@ -175,9 +185,13 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
is PortfolioFetcher.Mode.Wallet -> Unit
|
||||
is PortfolioFetcher.Mode.All -> {
|
||||
if (portfolioData.balances.size > 1) {
|
||||
val isAllSelected = portfolio.accountsBalance.accountStatuses
|
||||
.all { accountStatus -> accountStatus.account.accountId in selectedAccount }
|
||||
val walletTitle = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = "GroupTitle ${wallet.walletId.stringValue}",
|
||||
name = stringReference(wallet.name),
|
||||
isSelected = isAllSelected,
|
||||
onClick = { onWalletSelected(wallet.walletId, portfolioData, true) },
|
||||
deviceIcon = walletIconUMConverter.convert(getWalletIconUseCase(wallet)),
|
||||
)
|
||||
|
||||
|
|
@ -190,14 +204,20 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
val isEnabledByFeature = isEnabled(wallet, accountStatus)
|
||||
val account = accountStatus.account
|
||||
val accountBalance = accountStatus.tokenList.totalFiatBalance
|
||||
val isSelected = accountStatus.accountId in selectedAccount
|
||||
val endIcon = if (isSelected) {
|
||||
UserWalletItemUM.EndIcon.Checkmark
|
||||
} else {
|
||||
UserWalletItemUM.EndIcon.None
|
||||
}
|
||||
val accountItemUM = AccountPortfolioItemUMConverter(
|
||||
onClick = { selectorController.selectAccount(account.accountId) },
|
||||
onClick = { onAccountSelected(account.accountId) },
|
||||
appCurrency = appCurrency,
|
||||
accountBalance = accountBalance,
|
||||
isEnabled = isEnabledByFeature,
|
||||
endIcon = endIcon,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
).convert(account)
|
||||
val isSelected = accountStatus.isSelected(selectedAccount)
|
||||
add(PortfolioSelectorItemUM.Portfolio(accountItemUM, isSelected))
|
||||
}
|
||||
}
|
||||
|
|
@ -206,11 +226,64 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onAccountSelected(accountId: AccountId) {
|
||||
if (params.settings.isMultiChoice) {
|
||||
innerSelectedAccounts.update { selected ->
|
||||
if (accountId in selected) {
|
||||
selected - accountId
|
||||
} else {
|
||||
selected + accountId
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectorController.selectAccount(accountId)
|
||||
}
|
||||
}
|
||||
|
||||
// only isMultiChoice branch
|
||||
private fun onWalletSelected(
|
||||
walletId: UserWalletId,
|
||||
portfolioData: PortfolioFetcher.Data,
|
||||
isAccountMode: Boolean,
|
||||
) {
|
||||
if (params.settings.isMultiChoice) {
|
||||
val portfolio = portfolioData.balances[walletId] ?: return
|
||||
innerSelectedAccounts.update { selected ->
|
||||
val isAllSelected = if (isAccountMode) {
|
||||
portfolio.accountsBalance.accountStatuses
|
||||
.all { accountStatus -> accountStatus.account.accountId in selected }
|
||||
} else {
|
||||
portfolio.accountsBalance.mainAccount.account.accountId in selected
|
||||
}
|
||||
val walletAccounts = if (isAccountMode) {
|
||||
portfolio.accountsBalance.accountStatuses
|
||||
.mapTo(mutableSetOf()) { it.account.accountId }
|
||||
} else {
|
||||
mutableSetOf(portfolio.accountsBalance.mainAccount.account.accountId)
|
||||
}
|
||||
if (isAllSelected) {
|
||||
selected - walletAccounts
|
||||
} else {
|
||||
selected + walletAccounts
|
||||
}
|
||||
}
|
||||
} else {
|
||||
portfolioData.balances[walletId]?.accountsBalance?.mainAccount?.account?.accountId
|
||||
?.let { accountId -> selectorController.selectAccount(accountId) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onApplyClick() {
|
||||
selectorController.selectAccount(innerSelectedAccounts.value)
|
||||
}
|
||||
|
||||
private fun createLockedWallets(wallets: List<PortfolioSelectorItemUM>): List<PortfolioSelectorItemUM> {
|
||||
val lockedWalletsTitle = PortfolioSelectorItemUM.GroupTitle(
|
||||
id = "lockedWalletsTitleId",
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
|
||||
isSelected = false,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
return listOf(lockedWalletsTitle) + wallets
|
||||
|
|
@ -221,5 +294,6 @@ internal class PortfolioSelectorModel @Inject constructor(
|
|||
private fun emptyState() = PortfolioSelectorUM(
|
||||
items = persistentListOf(),
|
||||
title = TextReference.EMPTY,
|
||||
button = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -9,6 +9,12 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
data class PortfolioSelectorUM(
|
||||
val title: TextReference,
|
||||
val items: ImmutableList<PortfolioSelectorItemUM>,
|
||||
val button: PortfolioSelectorButtonUM?,
|
||||
)
|
||||
|
||||
data class PortfolioSelectorButtonUM(
|
||||
val text: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
|
@ -19,6 +25,8 @@ sealed interface PortfolioSelectorItemUM {
|
|||
override val id: String,
|
||||
val name: TextReference,
|
||||
val deviceIcon: DeviceIconUM,
|
||||
val isSelected: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
) : PortfolioSelectorItemUM
|
||||
|
||||
data class Portfolio(
|
||||
|
|
|
|||
|
|
@ -4,20 +4,18 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.unit.dp
|
||||
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.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorUM
|
||||
|
||||
@Composable
|
||||
|
|
@ -27,36 +25,29 @@ internal fun PortfolioSelectorBS(
|
|||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = onDismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
onBack = onBack,
|
||||
scrollableContent = false,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
containerColor = TangemTheme.colors3.bg.primary,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
title = {
|
||||
TangemModalBottomSheetTitle(
|
||||
TangemTopNavigation(
|
||||
title = state.title,
|
||||
startIconRes = R.drawable.ic_back_24,
|
||||
onStartClick = onBack,
|
||||
contentAlign = TangemTopNavigation.ContentAlign.Center,
|
||||
blurBackground = false,
|
||||
onClose = onDismiss,
|
||||
)
|
||||
},
|
||||
content = {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
PortfolioSelectorContentV2(
|
||||
state = state,
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
} else {
|
||||
PortfolioSelectorContent(
|
||||
state = state,
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
PortfolioSelectorContentV2(
|
||||
state = state,
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
modifier = modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -80,13 +71,11 @@ private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::cla
|
|||
@Composable
|
||||
private fun PreviewV2(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
PortfolioSelectorBS(
|
||||
state = params,
|
||||
onDismiss = {},
|
||||
modifier = Modifier,
|
||||
onBack = {},
|
||||
)
|
||||
}
|
||||
PortfolioSelectorBS(
|
||||
state = params,
|
||||
onDismiss = {},
|
||||
modifier = Modifier,
|
||||
onBack = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ 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
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorButtonUM
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorItemUM
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -168,12 +169,19 @@ internal object PortfolioSelectorPreviewData {
|
|||
private val lockedWalletItem: UserWalletItemUM
|
||||
get() = walletItem.copy(isEnabled = false)
|
||||
|
||||
val button = PortfolioSelectorButtonUM(
|
||||
text = resourceReference(R.string.common_apply),
|
||||
onClick = { },
|
||||
)
|
||||
|
||||
val firstList
|
||||
get() = listOf(
|
||||
PortfolioSelectorItemUM.GroupTitle(
|
||||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem 2.0"),
|
||||
deviceIcon = DeviceIconUM.Stub(cardsCount = 2),
|
||||
isSelected = false,
|
||||
onClick = {},
|
||||
),
|
||||
PortfolioSelectorItemUM.Portfolio(accountItem, false),
|
||||
PortfolioSelectorItemUM.Portfolio(lockedAccountItem, false),
|
||||
|
|
@ -181,6 +189,8 @@ internal object PortfolioSelectorPreviewData {
|
|||
id = UUID.randomUUID().toString(),
|
||||
name = stringReference("Tangem White"),
|
||||
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
|
||||
isSelected = false,
|
||||
onClick = {},
|
||||
),
|
||||
PortfolioSelectorItemUM.Portfolio(accountItem, true),
|
||||
)
|
||||
|
|
@ -191,6 +201,8 @@ internal object PortfolioSelectorPreviewData {
|
|||
id = UUID.randomUUID().toString(),
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
|
||||
isSelected = false,
|
||||
onClick = {},
|
||||
),
|
||||
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
|
||||
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
|
||||
|
|
@ -209,6 +221,8 @@ internal object PortfolioSelectorPreviewData {
|
|||
id = UUID.randomUUID().toString(),
|
||||
name = resourceReference(R.string.common_locked_wallets),
|
||||
deviceIcon = DeviceIconUM.Stub(cardsCount = 1),
|
||||
isSelected = false,
|
||||
onClick = {},
|
||||
),
|
||||
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
|
||||
PortfolioSelectorItemUM.Portfolio(lockedWalletItem, false),
|
||||
|
|
@ -220,18 +234,22 @@ internal class PortfolioSelectorPreviewStateProvider : CollectionPreviewParamete
|
|||
PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_account),
|
||||
items = PortfolioSelectorPreviewData.firstList.toImmutableList(),
|
||||
button = null,
|
||||
),
|
||||
PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_account),
|
||||
items = PortfolioSelectorPreviewData.secondList.toImmutableList(),
|
||||
button = PortfolioSelectorPreviewData.button,
|
||||
),
|
||||
PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_wallet),
|
||||
items = PortfolioSelectorPreviewData.walletList.toImmutableList(),
|
||||
button = null,
|
||||
),
|
||||
PortfolioSelectorUM(
|
||||
title = resourceReference(R.string.common_choose_wallet),
|
||||
items = PortfolioSelectorPreviewData.lockedWalletList.toImmutableList(),
|
||||
button = PortfolioSelectorPreviewData.button,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -32,7 +32,6 @@ 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
|
||||
import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorItemUM
|
||||
|
|
@ -218,16 +217,14 @@ private fun WalletNameRow(model: PortfolioSelectorItemUM.GroupTitle, modifier: M
|
|||
@Composable
|
||||
private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
PortfolioSelectorContentV2(
|
||||
state = params,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
PortfolioSelectorContentV2(
|
||||
state = params,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,9 @@ import com.tangem.core.decompose.factory.ComponentFactory
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
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.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
|
||||
|
|
@ -49,17 +47,10 @@ internal class TokenActionsComponent @AssistedInject constructor(
|
|||
val state = model.uiState.collectAsStateWithLifecycle()
|
||||
val bottomSheet by bottomSheetSlot.subscribeAsState()
|
||||
val tokenActionsUM = state.value ?: return
|
||||
if (LocalRedesignEnabled.current || params.isRedesignForced) {
|
||||
TokenActionsContentV2(
|
||||
modifier = modifier,
|
||||
state = tokenActionsUM,
|
||||
)
|
||||
} else {
|
||||
TokenActionsContent(
|
||||
modifier = modifier,
|
||||
state = tokenActionsUM,
|
||||
)
|
||||
}
|
||||
TokenActionsContentV2(
|
||||
modifier = modifier,
|
||||
state = tokenActionsUM,
|
||||
)
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ 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
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
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
|
||||
|
|
@ -41,7 +40,6 @@ import javax.inject.Inject
|
|||
@ModelScoped
|
||||
internal class TokenActionsUiBuilder @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val walletIconUMConverter: WalletIconUMConverter,
|
||||
|
|
@ -55,51 +53,12 @@ internal class TokenActionsUiBuilder @Inject constructor(
|
|||
isBalanceHidden: Boolean,
|
||||
bottomAction: BottomAction,
|
||||
): TokenActionsUM {
|
||||
return if (designFeatureToggles.isRedesignEnabled || params.isRedesignForced) {
|
||||
buildV2(
|
||||
cryptoCurrencyData = cryptoCurrencyData,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
appCurrency = appCurrency,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
bottomAction = bottomAction,
|
||||
)
|
||||
} else {
|
||||
buildV1(
|
||||
cryptoCurrencyData = cryptoCurrencyData,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
bottomAction = bottomAction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildV1(
|
||||
cryptoCurrencyData: CryptoCurrencyData,
|
||||
tokenActionsHandler: TokenActionsHandler,
|
||||
bottomAction: BottomAction,
|
||||
): TokenActionsUM {
|
||||
val status = cryptoCurrencyData.status
|
||||
val tokenUM = TokenItemState.Content(
|
||||
id = status.currency.id.value,
|
||||
iconState = CryptoCurrencyToIconStateConverter().convert(status.currency),
|
||||
titleState = TokenItemState.TitleState.Content(stringReference(status.currency.name)),
|
||||
fiatAmountState = null,
|
||||
subtitle2State = null,
|
||||
subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(status.currency.symbol)),
|
||||
onItemClick = null,
|
||||
onItemLongClick = null,
|
||||
)
|
||||
return TokenActionsUM(
|
||||
token = tokenUM,
|
||||
quickActions = quickActions(
|
||||
cryptoData = cryptoCurrencyData,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
isRedesignEnabled = false,
|
||||
context = params.context,
|
||||
),
|
||||
bottomActionText = bottomActionText(bottomAction),
|
||||
onBottomActionClick = {
|
||||
params.callbacks.onBottomActionClick(bottomAction)
|
||||
},
|
||||
return buildV2(
|
||||
cryptoCurrencyData = cryptoCurrencyData,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
appCurrency = appCurrency,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
bottomAction = bottomAction,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +85,6 @@ internal class TokenActionsUiBuilder @Inject constructor(
|
|||
quickActions = quickActions(
|
||||
cryptoData = cryptoCurrencyData,
|
||||
tokenActionsHandler = tokenActionsHandler,
|
||||
isRedesignEnabled = true,
|
||||
context = params.context,
|
||||
),
|
||||
bottomActionText = bottomActionText(bottomAction),
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ package com.tangem.features.commonfeatures.impl.tokenactions.ui
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
|
|
@ -119,8 +117,8 @@ private fun QuickActionsList(state: TokenActionsUM, modifier: Modifier = Modifie
|
|||
}
|
||||
AnimatedVisibility(
|
||||
visibleState = transitionState,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
val actionModifier = when (actionUM) {
|
||||
is QuickActionUM.V1.Buy, is QuickActionUM.V2.Buy ->
|
||||
|
|
@ -231,17 +229,15 @@ private fun WalletPortfolioRow(name: TextReference, deviceIcon: DeviceIconUM, mo
|
|||
@Composable
|
||||
private fun Preview(@PreviewParameter(TokenActionsContentPreviewProviderV2::class) state: TokenActionsUM) {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
TokenActionsContentV2(
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(TangemTheme.colors2.surface.level2)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
TokenActionsContentV2(
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue