Updated on 2026-08-14
This commit is contained in:
parent
3d2ef29a24
commit
4430add6f0
9 changed files with 221 additions and 6 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import java.util.Locale
|
|||
|
||||
object BigDecimalFormatter {
|
||||
|
||||
const val EMPTY_BALANCE_SIGN = "—"
|
||||
|
||||
private const val TEMP_CURRENCY_CODE = "USD"
|
||||
|
||||
fun formatCryptoAmount(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
|||
data class TokenDetailsState(
|
||||
val topAppBarConfig: TokenDetailsTopAppBarConfig,
|
||||
val tokenInfoBlockState: TokenInfoBlockState,
|
||||
val tokenBalanceBlockState: TokenDetailsBalanceBlockState,
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue