Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-19 14:15:27 +05:00
parent 3d2ef29a24
commit 4430add6f0
9 changed files with 221 additions and 6 deletions

View file

@ -1,6 +1,5 @@
package com.tangem.core.ui.components.buttons
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
@ -12,7 +11,6 @@ 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.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.buttons.actions.ActionButton
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
@ -24,12 +22,10 @@ import kotlinx.collections.immutable.persistentListOf
fun HorizontalActionChips(
buttons: ImmutableList<ActionButtonConfig>,
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(0.dp),
contentPadding: PaddingValues = PaddingValues(TangemTheme.dimens.spacing0),
) {
LazyRow(
modifier = modifier
.background(color = TangemTheme.colors.background.secondary)
.fillMaxWidth(),
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8),
verticalAlignment = Alignment.CenterVertically,
contentPadding = contentPadding,

View file

@ -8,6 +8,8 @@ import java.util.Locale
object BigDecimalFormatter {
const val EMPTY_BALANCE_SIGN = ""
private const val TEMP_CURRENCY_CODE = "USD"
fun formatCryptoAmount(

View file

@ -26,6 +26,8 @@ dependencies {
implementation(deps.compose.accompanist.systemUiController)
implementation(deps.compose.coil)
implementation(deps.kotlin.immutable.collections)
/** DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)

View file

@ -1,9 +1,13 @@
package com.tangem.feature.tokendetails.presentation.tokendetails
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
import com.tangem.features.tokendetails.impl.R
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
internal object TokenDetailsPreviewData {
@ -34,8 +38,42 @@ internal object TokenDetailsPreviewData {
),
)
private val actionButtons = persistentListOf(
ActionButtonConfig(
text = "Buy",
iconResId = R.drawable.ic_plus_24,
onClick = {},
),
ActionButtonConfig(
text = "Send",
iconResId = R.drawable.ic_arrow_up_24,
onClick = {},
),
ActionButtonConfig(
text = "Receive",
iconResId = R.drawable.ic_arrow_down_24,
onClick = {},
),
ActionButtonConfig(
text = "Exchange",
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = {},
),
)
private val disabledActionButtons = actionButtons.map { it.copy(enabled = false) }.toPersistentList()
val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = disabledActionButtons)
val balanceContent = TokenDetailsBalanceBlockState.Content(
actionButtons = actionButtons,
fiatBalance = "123,00$",
cryptoBalance = "866,96 USDT",
)
val balanceError = TokenDetailsBalanceBlockState.Error(actionButtons = disabledActionButtons)
val tokenDetailsState = TokenDetailsState(
topAppBarConfig = tokenDetailsTopAppBarConfig,
tokenInfoBlockState = tokenInfoBlockState,
tokenBalanceBlockState = balanceLoading,
)
}

View file

@ -0,0 +1,23 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import kotlinx.collections.immutable.ImmutableList
sealed class TokenDetailsBalanceBlockState {
abstract val actionButtons: ImmutableList<ActionButtonConfig>
data class Loading(
override val actionButtons: ImmutableList<ActionButtonConfig>,
) : TokenDetailsBalanceBlockState()
data class Content(
override val actionButtons: ImmutableList<ActionButtonConfig>,
val fiatBalance: String,
val cryptoBalance: String,
) : TokenDetailsBalanceBlockState()
data class Error(
override val actionButtons: ImmutableList<ActionButtonConfig>,
) : TokenDetailsBalanceBlockState()
}

View file

@ -3,4 +3,5 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state
data class TokenDetailsState(
val topAppBarConfig: TokenDetailsTopAppBarConfig,
val tokenInfoBlockState: TokenInfoBlockState,
val tokenBalanceBlockState: TokenDetailsBalanceBlockState,
)

View file

@ -1,5 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@ -10,6 +11,7 @@ import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock
@ -24,8 +26,10 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
.padding(paddingValues = scaffoldPaddings)
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
TokenInfoBlock(state = state.tokenInfoBlockState)
TokenDetailsBalanceBlock(state = state.tokenBalanceBlockState)
}
}
}

View file

@ -0,0 +1,136 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.buttons.HorizontalActionChips
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
import com.tangem.features.tokendetails.impl.R
@Composable
internal fun TokenDetailsBalanceBlock(state: TokenDetailsBalanceBlockState, modifier: Modifier = Modifier) {
Surface(
modifier = modifier.fillMaxWidth(),
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
) {
Column {
Text(
modifier = Modifier
.padding(
top = TangemTheme.dimens.spacing12,
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
),
text = stringResource(id = R.string.onboarding_balance_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
maxLines = 1,
)
FiatBalance(
state = state,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing4)
.padding(horizontal = TangemTheme.dimens.spacing12),
)
CryptoBalance(
state = state,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing4)
.padding(horizontal = TangemTheme.dimens.spacing12),
)
HorizontalActionChips(
buttons = state.actionButtons,
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing12),
)
}
}
}
@Composable
private fun FiatBalance(state: TokenDetailsBalanceBlockState, modifier: Modifier = Modifier) {
when (state) {
is TokenDetailsBalanceBlockState.Loading -> RectangleShimmer(
modifier = modifier.size(
width = TangemTheme.dimens.size102,
height = TangemTheme.dimens.size24,
),
)
is TokenDetailsBalanceBlockState.Content -> Text(
modifier = modifier,
text = state.fiatBalance,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
is TokenDetailsBalanceBlockState.Error -> Text(
modifier = modifier,
text = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
}
}
@Composable
private fun CryptoBalance(state: TokenDetailsBalanceBlockState, modifier: Modifier = Modifier) {
when (state) {
is TokenDetailsBalanceBlockState.Loading -> RectangleShimmer(
modifier = modifier.size(
width = TangemTheme.dimens.size70,
height = TangemTheme.dimens.size16,
),
)
is TokenDetailsBalanceBlockState.Content -> Text(
modifier = modifier,
text = state.cryptoBalance,
style = TangemTheme.typography.caption,
color = TangemTheme.colors.text.primary1,
)
is TokenDetailsBalanceBlockState.Error -> Text(
modifier = modifier,
text = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
style = TangemTheme.typography.caption,
color = TangemTheme.colors.text.primary1,
)
}
}
@Preview
@Composable
private fun Preview_TokenDetailsBalanceBlock_LightTheme(
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,
) {
TangemTheme(isDark = false) {
TokenDetailsBalanceBlock(state)
}
}
@Preview
@Composable
private fun Preview_TokenDetailsBalanceBlock_DarkTheme(
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,
) {
TangemTheme(isDark = true) {
TokenDetailsBalanceBlock(state)
}
}
private class TokenDetailsBalanceBlockStateProvider : CollectionPreviewParameterProvider<TokenDetailsBalanceBlockState>(
collection = listOf(
TokenDetailsPreviewData.balanceLoading,
TokenDetailsPreviewData.balanceContent,
TokenDetailsPreviewData.balanceError,
),
)

View file

@ -4,12 +4,17 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.properties.Delegates
private const val LOADING_DELAY = 4_000L
@HiltViewModel
internal class TokenDetailsViewModel @Inject constructor() : ViewModel() {
@ -18,6 +23,14 @@ internal class TokenDetailsViewModel @Inject constructor() : ViewModel() {
var uiState by mutableStateOf(getInitialState())
private set
init {
// simulate loading state
viewModelScope.launch {
delay(LOADING_DELAY)
uiState = uiState.copy(tokenBalanceBlockState = TokenDetailsPreviewData.balanceContent)
}
}
private fun getInitialState() = TokenDetailsPreviewData.tokenDetailsState.copy(
topAppBarConfig = TokenDetailsPreviewData.tokenDetailsTopAppBarConfig.copy(
onBackClick = ::onBackClick,