Updated on 2026-08-14
This commit is contained in:
parent
7f4454c8d2
commit
98d0d9c75e
39 changed files with 266 additions and 3536 deletions
|
|
@ -15,13 +15,11 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreenLegacy
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.ChooseAddressBottomSheetComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.CloreMigrationBottomSheetComponent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.DynamicAddressesBottomSheetComponent
|
||||
|
|
@ -154,31 +152,18 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
val txHistoryDetails by txHistoryDetailsSlot.subscribeAsState()
|
||||
NavigationBar3ButtonsScrim()
|
||||
|
||||
if (LocalRedesignEnabled.current) {
|
||||
val tokenDetailsUM by model.redesignUiState.collectAsStateWithLifecycle()
|
||||
val tokenDetailsUM by model.redesignUiState.collectAsStateWithLifecycle()
|
||||
|
||||
TokenDetailsScreen(
|
||||
tokenDetailsUM = tokenDetailsUM,
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||
yieldSupplyComponent = yieldSupplyComponent,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
ratingComponent = ratingSlotState.child?.instance,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
TokenDetailsScreenLegacy(
|
||||
state = state,
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
yieldSupplyComponent = yieldSupplyComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
ratingComponent = ratingSlotState.child?.instance,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
)
|
||||
}
|
||||
TokenDetailsScreen(
|
||||
tokenDetailsUM = tokenDetailsUM,
|
||||
tokenMarketBlockComponent = tokenMarketBlockComponent,
|
||||
yieldSupplyComponent = yieldSupplyComponent,
|
||||
txHistoryComponent = txHistoryComponent,
|
||||
expressTransactionsComponent = expressTransactionsComponent,
|
||||
ratingComponent = ratingSlotState.child?.instance,
|
||||
marketingBannerComponent = marketingBannerComponent,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
txHistoryDetails.child?.instance?.BottomSheet()
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ 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.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.core.ui.ds.image.DeviceIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -184,7 +183,6 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private val getWalletIconUseCase: GetWalletIconUseCase,
|
||||
private val walletIconUMConverter: WalletIconUMConverter,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val redesignStateController: TokenDetailsStateController,
|
||||
private val swapFeedbackUseCase: SwapFeedbackUseCase,
|
||||
private val quickTopUpBlockFactory: QuickTopUpBlockFactory,
|
||||
|
|
@ -346,10 +344,8 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
uiState.value = stateFactory.getStateWithUpdatedHidden(
|
||||
isBalanceHidden = settings.isBalanceHidden,
|
||||
)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
redesignStateController.update { state ->
|
||||
state.copy(isBalanceHidden = settings.isBalanceHidden)
|
||||
}
|
||||
redesignStateController.update { state ->
|
||||
state.copy(isBalanceHidden = settings.isBalanceHidden)
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -366,38 +362,36 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
latestTokenActions = state.states
|
||||
sendButtonsEvents(state.states)
|
||||
uiState.value = stateFactory.getManageButtonsState(actions = state.states)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
val networkSource = currencyStatus.value.sources.networkSource
|
||||
redesignStateController.update(
|
||||
UpdateActionButtonsTransformer(
|
||||
actions = state.states,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateAddFundsTransformer(
|
||||
actions = state.states,
|
||||
networkSource = networkSource,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
onActionDispatched = bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateTransferTransformer(
|
||||
actions = state.states,
|
||||
networkSource = networkSource,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
analyticsEventHandler = analyticsEventsHandler,
|
||||
onActionDispatched = bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateZeroBalanceActionsTransformer(
|
||||
actions = state.states,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
),
|
||||
)
|
||||
}
|
||||
val networkSource = currencyStatus.value.sources.networkSource
|
||||
redesignStateController.update(
|
||||
UpdateActionButtonsTransformer(
|
||||
actions = state.states,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateAddFundsTransformer(
|
||||
actions = state.states,
|
||||
networkSource = networkSource,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
onActionDispatched = bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateTransferTransformer(
|
||||
actions = state.states,
|
||||
networkSource = networkSource,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
analyticsEventHandler = analyticsEventsHandler,
|
||||
onActionDispatched = bottomSheetNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
redesignStateController.update(
|
||||
UpdateZeroBalanceActionsTransformer(
|
||||
actions = state.states,
|
||||
clickIntents = this@TokenDetailsModel,
|
||||
),
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
|
|
@ -1502,7 +1496,6 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun initRedesign() {
|
||||
if (!designFeatureToggles.isRedesignEnabled) return
|
||||
initRedesignState()
|
||||
observeRedesignBalance()
|
||||
updateRedesignTopBarMenu()
|
||||
|
|
|
|||
|
|
@ -1,261 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScaffoldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState
|
||||
import com.tangem.features.rating.RatingComponent
|
||||
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlock
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemeRedesign
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlockLegacy
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlockLegacy
|
||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||
import com.tangem.features.marketing.api.MarketingBannerComponent
|
||||
import com.tangem.features.tokendetails.ExpressTransactionsComponent
|
||||
import com.tangem.features.txhistory.component.TxHistoryComponent
|
||||
import com.tangem.features.txhistory.entity.TxHistoryItemsUM
|
||||
import com.tangem.features.txhistory.entity.TxHistoryUM
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyComponent
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
// TODO: Split to blocks [REDACTED_JIRA]
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "LongParameterList")
|
||||
@Composable
|
||||
internal fun TokenDetailsScreenLegacy(
|
||||
state: TokenDetailsState,
|
||||
tokenMarketBlockComponent: TokenMarketBlockComponent?,
|
||||
txHistoryComponent: TxHistoryComponent,
|
||||
yieldSupplyComponent: YieldSupplyComponent,
|
||||
expressTransactionsComponent: ExpressTransactionsComponent,
|
||||
ratingComponent: RatingComponent?,
|
||||
marketingBannerComponent: MarketingBannerComponent,
|
||||
) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
Scaffold(
|
||||
topBar = { TokenDetailsTopAppBar(config = state.topAppBarConfig) },
|
||||
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
|
||||
containerColor = TangemTheme.colors.background.secondary,
|
||||
) { scaffoldPaddings ->
|
||||
val listState = rememberLazyListState()
|
||||
val txHistoryComponentState by txHistoryComponent.legacyTxHistoryState.collectAsStateWithLifecycle()
|
||||
val expressState by expressTransactionsComponent.state.collectAsStateWithLifecycle()
|
||||
val betweenItemsPadding = TangemTheme.dimens.spacing12
|
||||
val horizontalPadding = TangemTheme.dimens.spacing16
|
||||
val itemModifier = Modifier
|
||||
.padding(top = betweenItemsPadding)
|
||||
.padding(horizontal = horizontalPadding)
|
||||
|
||||
TangemPullToRefreshContainer(
|
||||
config = state.pullToRefreshConfig,
|
||||
modifier = Modifier.padding(scaffoldPaddings),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.testTag(TokenDetailsScreenTestTags.SCREEN_CONTAINER),
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(
|
||||
bottom = TangemTheme.dimens.spacing16 + bottomBarHeight,
|
||||
),
|
||||
) {
|
||||
item {
|
||||
TokenInfoBlock(
|
||||
modifier = Modifier.padding(horizontal = horizontalPadding),
|
||||
state = state.tokenInfoBlockState,
|
||||
)
|
||||
}
|
||||
item {
|
||||
TokenDetailsBalanceBlockLegacy(
|
||||
modifier = itemModifier,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
state = state.tokenBalanceBlockState,
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = state.notifications,
|
||||
key = { it::class.java },
|
||||
contentType = { it.config::class.java },
|
||||
itemContent = { notification ->
|
||||
Notification(
|
||||
modifier = itemModifier.animateItem(),
|
||||
config = notification.config,
|
||||
iconTint = when (notification) {
|
||||
is TokenDetailsNotification.Informational -> TangemTheme.colors.icon.accent
|
||||
is TokenDetailsNotification.UsedOutdatedData -> TangemTheme.colors.text.attention
|
||||
else -> null
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
when {
|
||||
tokenMarketBlockComponent != null -> {
|
||||
item(
|
||||
key = TokenMarketBlockComponent::class.java,
|
||||
contentType = TokenMarketBlockComponent::class.java,
|
||||
content = { tokenMarketBlockComponent.Content(modifier = itemModifier) },
|
||||
)
|
||||
}
|
||||
state.isMarketPriceAvailable -> {
|
||||
item(
|
||||
key = MarketPriceBlockState::class.java,
|
||||
contentType = MarketPriceBlockState::class.java,
|
||||
content = {
|
||||
MarketPriceBlock(
|
||||
modifier = itemModifier,
|
||||
state = state.marketPriceBlockState,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.stakingBlocksState != null) {
|
||||
item(
|
||||
key = StakingBlockUM::class.java,
|
||||
contentType = StakingBlockUM::class.java,
|
||||
content = {
|
||||
TokenStakingBlockLegacy(
|
||||
state = state.stakingBlocksState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
yieldSupplyComponent.Content(modifier = itemModifier)
|
||||
}
|
||||
|
||||
item(key = "marketing_banner_block") {
|
||||
TangemThemeRedesign {
|
||||
marketingBannerComponent.Content(modifier = itemModifier)
|
||||
}
|
||||
}
|
||||
|
||||
with(expressTransactionsComponent) {
|
||||
expressTransactionsContentLegacy(
|
||||
state = expressState.transactionsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
|
||||
state.quickTopUpBlock?.let { quickTopUpBlock ->
|
||||
item(key = "quick_top_up_block") {
|
||||
TangemThemeRedesign {
|
||||
QuickTopUpBlock(
|
||||
state = quickTopUpBlock,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with(txHistoryComponent) {
|
||||
txHistoryContentLegacy(listState = listState, state = txHistoryComponentState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expressState.bottomSheetSlot?.content(
|
||||
ratingComponent?.let { comp -> { comp.Content(modifier = Modifier.fillMaxWidth()) } },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun TokenDetailsScreenPreview(
|
||||
@PreviewParameter(TokenDetailsScreenParameterProvider::class) state: TokenDetailsState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TokenDetailsScreenLegacy(
|
||||
state = state,
|
||||
tokenMarketBlockComponent = null,
|
||||
txHistoryComponent = object : TxHistoryComponent {
|
||||
override val legacyTxHistoryState: StateFlow<TxHistoryUM> = MutableStateFlow(
|
||||
value = TxHistoryUM.Empty(isBalanceHidden = false, onExploreClick = {}),
|
||||
)
|
||||
|
||||
override val txHistoryState: StateFlow<TxHistoryItemsUM> = MutableStateFlow(
|
||||
value = TxHistoryItemsUM.Empty(isBalanceHidden = false, onExploreClick = {}),
|
||||
)
|
||||
|
||||
override fun LazyListScope.txHistoryContentLegacy(listState: LazyListState, state: TxHistoryUM) = Unit
|
||||
|
||||
override fun LazyListScope.txHistoryContent(listState: LazyListState, state: TxHistoryItemsUM) = Unit
|
||||
},
|
||||
yieldSupplyComponent = object : YieldSupplyComponent {
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
}
|
||||
},
|
||||
expressTransactionsComponent = PreviewExpressTransactionsComponent,
|
||||
ratingComponent = null,
|
||||
marketingBannerComponent = object : MarketingBannerComponent {
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) = Unit
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val PreviewExpressTransactionsComponent = object : ExpressTransactionsComponent {
|
||||
override val state: StateFlow<ExpressTransactionsBlockState> = MutableStateFlow(
|
||||
ExpressTransactionsBlockState(
|
||||
transactions = persistentListOf(),
|
||||
transactionsToDisplay = persistentListOf(),
|
||||
bottomSheetSlot = null,
|
||||
),
|
||||
)
|
||||
|
||||
override fun LazyListScope.expressTransactionsContentLegacy(
|
||||
state: PersistentList<ExpressTransactionStateUM>,
|
||||
modifier: Modifier,
|
||||
) = Unit
|
||||
|
||||
override fun LazyListScope.expressTransactionsContent(
|
||||
state: PersistentList<ExpressTransactionStateUM>,
|
||||
modifier: Modifier,
|
||||
) = Unit
|
||||
}
|
||||
|
||||
private class TokenDetailsScreenParameterProvider : CollectionPreviewParameterProvider<TokenDetailsState>(
|
||||
collection = listOf(
|
||||
TokenDetailsPreviewData.tokenDetailsState_1,
|
||||
TokenDetailsPreviewData.tokenDetailsState_2,
|
||||
TokenDetailsPreviewData.tokenDetailsState_3,
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.buttons.HorizontalActionChips
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.components.text.TextAnimatedCounter
|
||||
import com.tangem.core.ui.components.text.applyBladeBrush
|
||||
import com.tangem.core.ui.extensions.orMaskWithStars
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsYieldSupplyState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries")
|
||||
@Composable
|
||||
internal fun TokenDetailsBalanceBlockLegacy(
|
||||
state: TokenDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
val spacing4 = TangemTheme.dimens.spacing4
|
||||
val spacing10 = TangemTheme.dimens.spacing10
|
||||
val spacing12 = TangemTheme.dimens.spacing12
|
||||
|
||||
ConstraintLayout(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val (balanceTitle, toggleButtons, fiatBalance, cryptoBalance, actionChips) = createRefs()
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.common_balance_title),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
modifier = Modifier.constrainAs(balanceTitle) {
|
||||
top.linkTo(anchor = parent.top, margin = spacing12)
|
||||
start.linkTo(anchor = parent.start, margin = spacing12)
|
||||
},
|
||||
)
|
||||
|
||||
BalanceButtons(
|
||||
state = state,
|
||||
modifier = Modifier.constrainAs(toggleButtons) {
|
||||
top.linkTo(anchor = parent.top)
|
||||
end.linkTo(anchor = parent.end, margin = spacing10)
|
||||
},
|
||||
)
|
||||
|
||||
FiatBalance(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.constrainAs(fiatBalance) {
|
||||
top.linkTo(anchor = balanceTitle.bottom, margin = spacing4)
|
||||
start.linkTo(anchor = parent.start, margin = spacing12)
|
||||
},
|
||||
)
|
||||
|
||||
CryptoBalance(
|
||||
state = state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.constrainAs(cryptoBalance) {
|
||||
top.linkTo(anchor = fiatBalance.bottom, margin = spacing4)
|
||||
start.linkTo(anchor = parent.start, margin = spacing12)
|
||||
},
|
||||
)
|
||||
|
||||
HorizontalActionChips(
|
||||
buttons = state.actionButtons.map(TokenDetailsActionButton::config).toImmutableList(),
|
||||
modifier = Modifier.constrainAs(actionChips) {
|
||||
top.linkTo(anchor = cryptoBalance.bottom, margin = spacing12)
|
||||
start.linkTo(anchor = parent.start)
|
||||
end.linkTo(anchor = parent.end)
|
||||
bottom.linkTo(anchor = parent.bottom, margin = spacing12)
|
||||
},
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FiatBalance(
|
||||
state: TokenDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state) {
|
||||
is TokenDetailsBalanceBlockState.Loading -> RectangleShimmer(
|
||||
modifier = modifier.size(
|
||||
width = TangemTheme.dimens.size102,
|
||||
height = TangemTheme.dimens.size32,
|
||||
),
|
||||
)
|
||||
is TokenDetailsBalanceBlockState.Content -> if (state.displayYieldSupplyFiatBalance != null &&
|
||||
!isBalanceHidden
|
||||
) {
|
||||
TextAnimatedCounter(
|
||||
modifier = modifier,
|
||||
text = state.displayYieldSupplyFiatBalance,
|
||||
style = TangemTheme.typography.h2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = (state.displayYieldSupplyFiatBalance ?: state.displayFiatBalance).orMaskWithStars(
|
||||
isBalanceHidden,
|
||||
),
|
||||
style = TangemTheme.typography.h2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.primary1,
|
||||
),
|
||||
)
|
||||
}
|
||||
is TokenDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = modifier,
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CryptoBalance(
|
||||
state: TokenDetailsBalanceBlockState,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (state) {
|
||||
is TokenDetailsBalanceBlockState.Loading -> RectangleShimmer(
|
||||
modifier = modifier.size(
|
||||
width = TangemTheme.dimens.size70,
|
||||
height = TangemTheme.dimens.size16,
|
||||
),
|
||||
)
|
||||
is TokenDetailsBalanceBlockState.Content -> {
|
||||
Crossfade(
|
||||
modifier = modifier,
|
||||
targetState = state.yieldSupplyState,
|
||||
) { yieldSupplyState ->
|
||||
when (yieldSupplyState) {
|
||||
is TokenDetailsYieldSupplyState.Active -> {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
painter = painterResource(id = R.drawable.ic_exchange_horizontal_24),
|
||||
tint = TangemTheme.colors.icon.inactive,
|
||||
contentDescription = null,
|
||||
)
|
||||
TextAnimatedCounter(
|
||||
text = (state.displayYieldSupplyCryptoBalance ?: state.displayCryptoBalance)
|
||||
.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.caption2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.tertiary,
|
||||
),
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(bounded = false),
|
||||
onClick = { yieldSupplyState.yieldInfoClick() },
|
||||
),
|
||||
painter = painterResource(id = R.drawable.ic_information_24),
|
||||
tint = TangemTheme.colors.icon.inactive,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
is TokenDetailsYieldSupplyState.Empty -> Text(
|
||||
text = state.displayCryptoBalance.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.caption2.applyBladeBrush(
|
||||
isEnabled = state.isBalanceFlickering,
|
||||
textColor = TangemTheme.colors.text.tertiary,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
is TokenDetailsBalanceBlockState.Error -> Text(
|
||||
modifier = modifier,
|
||||
text = DASH_SIGN.orMaskWithStars(isBalanceHidden),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BalanceButtons(state: TokenDetailsBalanceBlockState, modifier: Modifier = Modifier) {
|
||||
if (state !is TokenDetailsBalanceBlockState.Content || !state.isBalanceSelectorEnabled) return
|
||||
|
||||
SegmentedButtons(
|
||||
config = state.balanceSegmentedButtonConfig,
|
||||
onClick = state.onBalanceSelect,
|
||||
showIndication = false,
|
||||
modifier = modifier
|
||||
.padding(top = TangemTheme.dimens.spacing11)
|
||||
.width(IntrinsicSize.Min),
|
||||
) { config ->
|
||||
val style = if (state.selectedBalanceType == config.type) {
|
||||
TangemTheme.typography.caption1
|
||||
} else {
|
||||
TangemTheme.typography.caption2
|
||||
}
|
||||
Text(
|
||||
text = config.title.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing6,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
)
|
||||
.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 152)
|
||||
@Preview(widthDp = 328, heightDp = 152, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_TokenDetailsBalanceBlock(
|
||||
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TokenDetailsBalanceBlockLegacy(state = state, isBalanceHidden = false)
|
||||
}
|
||||
}
|
||||
|
||||
private class TokenDetailsBalanceBlockStateProvider : CollectionPreviewParameterProvider<TokenDetailsBalanceBlockState>(
|
||||
collection = listOf(
|
||||
TokenDetailsPreviewData.balanceLoading,
|
||||
TokenDetailsPreviewData.balanceContent,
|
||||
TokenDetailsPreviewData.balanceContent.copy(isBalanceFlickering = true),
|
||||
TokenDetailsPreviewData.balanceContent.copy(
|
||||
yieldSupplyState = TokenDetailsYieldSupplyState.Active({}),
|
||||
),
|
||||
TokenDetailsPreviewData.balanceError,
|
||||
),
|
||||
)
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.TokenDetailsScreenTestTags
|
||||
import com.tangem.core.ui.utils.getGreyScaleColorFilter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingAvailableBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingBalanceBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingLoadingBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingTemporaryUnavailableBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.CurrencyIcon
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.StringsSigns
|
||||
|
||||
/**
|
||||
* Token staking block
|
||||
*
|
||||
* @param state component state
|
||||
* @param isBalanceHidden whether to hide balance
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
internal fun TokenStakingBlockLegacy(state: StakingBlockUM, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.clickable(
|
||||
enabled = state is StakingBlockUM.Staked,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
onClick = { (state as? StakingBlockUM.Staked)?.onStakeClicked?.invoke() },
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
AnimatedContent(
|
||||
targetState = state,
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
label = "Staking block animation",
|
||||
) { stakingBlock ->
|
||||
when (stakingBlock) {
|
||||
is StakingBlockUM.TemporaryUnavailable -> StakingTemporaryUnavailableBlock()
|
||||
is StakingBlockUM.Loading -> StakingLoading()
|
||||
is StakingBlockUM.Staked -> StakingBalanceBlock(
|
||||
state = stakingBlock,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
is StakingBlockUM.StakeAvailable -> StakingAvailableContent(
|
||||
state = stakingBlock,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(TokenDetailsScreenTestTags.STAKING_AVAILABLE_BLOCK),
|
||||
) {
|
||||
Row {
|
||||
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
|
||||
getGreyScaleColorFilter(state.iconState.isGrayscale)
|
||||
}
|
||||
CurrencyIcon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.align(Alignment.CenterVertically)
|
||||
.testTag(TokenDetailsScreenTestTags.STAKING_CURRENCY_ICON),
|
||||
icon = state.iconState,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
SpacerW8()
|
||||
Column {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||
Text(
|
||||
text = state.titleText.resolveReference(),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TITLE),
|
||||
)
|
||||
if (state.isBetaMode) {
|
||||
Text(
|
||||
text = StringsSigns.DOT,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.beta_mode_warning_title),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
|
||||
|
||||
Text(
|
||||
text = state.subtitleText.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier.testTag(TokenDetailsScreenTestTags.STAKING_SERVICE_TEXT),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
|
||||
}
|
||||
}
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResourceSafe(id = R.string.common_stake),
|
||||
enabled = state.isEnabled,
|
||||
onClick = state.onStakeClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingLoading(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
Row {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size20),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size36),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
|
||||
}
|
||||
}
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size48),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_TokenStakingBlock(
|
||||
@PreviewParameter(StakingBlockStateProvider::class)
|
||||
state: StakingBlockUM,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TokenStakingBlockLegacy(
|
||||
state = state,
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class StakingBlockStateProvider : CollectionPreviewParameterProvider<StakingBlockUM>(
|
||||
collection = listOf(
|
||||
stakingLoadingBlock,
|
||||
stakingAvailableBlock,
|
||||
stakingTemporaryUnavailableBlock,
|
||||
stakingBalanceBlock,
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
Loading…
Add table
Add a link
Reference in a new issue