diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt index 56b0ce8bf1..b76e11c37c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/TokenListItem.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.fields.SearchBar import com.tangem.core.ui.components.token.TokenItem +import com.tangem.core.ui.components.tokenlist.internal.GroupTitleItem import com.tangem.core.ui.components.tokenlist.internal.NetworkTitleItem import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.resolveReference @@ -25,6 +26,9 @@ fun TokenListItem(state: TokensListItemUM, isBalanceHidden: Boolean, modifier: M is TokensListItemUM.NetworkGroupTitle -> { NetworkTitleItem(networkName = state.name.resolveReference(), modifier = modifier) } + is TokensListItemUM.GroupTitle -> { + GroupTitleItem(state, modifier) + } is TokensListItemUM.Token -> { TokenItem( state = state.state, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt new file mode 100644 index 0000000000..c79e40c723 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/internal/GroupTitleItem.kt @@ -0,0 +1,31 @@ +package com.tangem.core.ui.components.tokenlist.internal + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.rows.NetworkTitle +import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +/** + * Group title item + * + * @param state state + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +@Composable +fun GroupTitleItem(state: TokensListItemUM.GroupTitle, modifier: Modifier = Modifier) { + NetworkTitle( + title = { + Text( + text = state.text.resolveReference(), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + }, + modifier = modifier, + ) +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt index 8a26f00508..2e9417d5ed 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/tokenlist/state/TokensListItemUM.kt @@ -29,8 +29,17 @@ sealed interface TokensListItemUM { * @property id id * @property name network group name */ + @Deprecated("Use GroupTitle instead") // TODO: [REDACTED_JIRA] data class NetworkGroupTitle(override val id: Int, val name: TextReference) : TokensListItemUM + /** + * Group title + * + * @property id id + * @property text title value + */ + data class GroupTitle(override val id: Any, val text: TextReference) : TokensListItemUM + /** * Token item * diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt index 653883fb90..8d66dfa574 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/ui/OnrampSelectToken.kt @@ -2,11 +2,12 @@ package com.tangem.features.onramp.selecttoken.ui import androidx.activity.compose.BackHandler import androidx.annotation.StringRes +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -16,6 +17,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun OnrampSelectToken( @StringRes titleResId: Int, @@ -25,21 +27,25 @@ internal fun OnrampSelectToken( ) { BackHandler(onBack = onBackClick) - Column( + LazyColumn( modifier = modifier .background(TangemTheme.colors.background.secondary) .imePadding() .systemBarsPadding(), ) { - AppBarWithBackButton( - onBackClick = onBackClick, - text = stringResource(id = titleResId), - iconRes = R.drawable.ic_close_24, - ) + stickyHeader(key = "header") { + AppBarWithBackButton( + onBackClick = onBackClick, + text = stringResource(id = titleResId), + iconRes = R.drawable.ic_close_24, + containerColor = TangemTheme.colors.background.secondary, + ) + } - onrampTokenListComponent.Content( - contentPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp), - modifier = Modifier, - ) + item(key = "token_list", contentType = "token_list") { + onrampTokenListComponent.Content( + modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp), + ) + } } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt index b4cef2c2bb..e427825e21 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt @@ -15,8 +15,8 @@ internal sealed interface ExchangeCardUM { /** Title reference */ val titleReference: TextReference - /** Flag that indicates if remove button should be shown */ - val hasRemoveButton: Boolean + /** Remove button UI model */ + val removeButtonUM: RemoveButtonUM? /** Token item state */ val tokenItemState: TokenItemState @@ -26,11 +26,9 @@ internal sealed interface ExchangeCardUM { * * @property titleReference title reference */ - data class Empty( - override val titleReference: TextReference, - ) : ExchangeCardUM { + data class Empty(override val titleReference: TextReference) : ExchangeCardUM { - override val hasRemoveButton: Boolean = false + override val removeButtonUM: RemoveButtonUM? = null // TODO: [REDACTED_JIRA] override val tokenItemState: TokenItemState = TokenItemState.Content( @@ -49,13 +47,14 @@ internal sealed interface ExchangeCardUM { * Filled * * @property titleReference title reference + * @property removeButtonUM remove button UI model * @property tokenItemState token item state */ data class Filled( override val titleReference: TextReference, + override val removeButtonUM: RemoveButtonUM?, override val tokenItemState: TokenItemState, - ) : ExchangeCardUM { + ) : ExchangeCardUM - override val hasRemoveButton: Boolean = true - } + data class RemoveButtonUM(val onClick: () -> Unit) } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt index 6d4aae2867..92a3229445 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt @@ -1,13 +1,12 @@ package com.tangem.features.onramp.swap.ui +import android.content.res.Configuration +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.* import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable @@ -24,6 +23,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.rows.NetworkTitle import com.tangem.core.ui.components.token.TokenItem import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.res.TangemTheme @@ -51,34 +51,41 @@ internal fun ExchangeCard(state: ExchangeCardUM, modifier: Modifier = Modifier) ), verticalArrangement = Arrangement.SpaceBetween, ) { - NetworkTitle( - title = { - Text( - text = state.titleReference.resolveReference(), - color = TangemTheme.colors.text.tertiary, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - style = TangemTheme.typography.subtitle2, - ) - }, - action = { - RemoveButton(hasRemoveButton = state.hasRemoveButton, onClick = {}) - }, - ) + Title(titleReference = state.titleReference, removeButtonUM = state.removeButtonUM) - TokenItem(state = state.tokenItemState, isBalanceHidden = false) + AnimatedContent(targetState = state.tokenItemState, label = "TokenItem's changing ") { + TokenItem(state = it, isBalanceHidden = false) + } } } @Composable -private fun RemoveButton(hasRemoveButton: Boolean, onClick: () -> Unit) { - AnimatedVisibility(visible = hasRemoveButton) { +private fun Title(titleReference: TextReference, removeButtonUM: ExchangeCardUM.RemoveButtonUM?) { + NetworkTitle( + title = { + Text( + text = titleReference.resolveReference(), + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTheme.typography.subtitle2, + ) + }, + action = { RemoveButton(state = removeButtonUM) }, + ) +} + +@Composable +private fun RemoveButton(state: ExchangeCardUM.RemoveButtonUM?) { + AnimatedVisibility(visible = state != null) { + state ?: return@AnimatedVisibility + Text( text = stringResource(id = R.string.manage_tokens_remove), modifier = Modifier.clickable( indication = ripple(bounded = false), interactionSource = remember { MutableInteractionSource() }, - onClick = onClick, + onClick = state.onClick, ), color = TangemTheme.colors.text.accent, maxLines = 1, @@ -88,22 +95,32 @@ private fun RemoveButton(hasRemoveButton: Boolean, onClick: () -> Unit) { } } -@Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun Preview_ExchangeCard(@PreviewParameter(ExchangeCardUMProvider::class) state: ExchangeCardUM) { TangemThemePreview { - ExchangeCard(state = state) + ExchangeCard( + state = state, + modifier = Modifier + .background(TangemTheme.colors.background.secondary) + .padding(16.dp), + ) } } private class ExchangeCardUMProvider : PreviewParameterProvider { override val values: Sequence = sequenceOf( - ExchangeCardUM.Empty( - titleReference = resourceReference(id = R.string.swapping_from_title), - ), - ExchangeCardUM.Filled( + ExchangeCardUM.Empty(titleReference = resourceReference(id = R.string.swapping_from_title)), + createFilled(removeButtonUM = null), + createFilled(removeButtonUM = ExchangeCardUM.RemoveButtonUM { }), + ) + + private fun createFilled(removeButtonUM: ExchangeCardUM.RemoveButtonUM?): ExchangeCardUM.Filled { + return ExchangeCardUM.Filled( titleReference = resourceReference(id = R.string.swapping_from_title), + removeButtonUM = removeButtonUM, tokenItemState = TokenItemState.Content( id = "1", iconState = CurrencyIconState.Locked, @@ -118,6 +135,6 @@ private class ExchangeCardUMProvider : PreviewParameterProvider onItemClick = {}, onItemLongClick = {}, ), - ), - ) + ) + } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/DefaultOnrampTokenListComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/DefaultOnrampTokenListComponent.kt index 67ada9b94b..c538d475cd 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/DefaultOnrampTokenListComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/DefaultOnrampTokenListComponent.kt @@ -1,6 +1,5 @@ package com.tangem.features.onramp.tokenlist -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue @@ -23,10 +22,10 @@ internal class DefaultOnrampTokenListComponent @AssistedInject constructor( private val model: OnrampTokenListModel = getOrCreateModel(params) @Composable - override fun Content(contentPadding: PaddingValues, modifier: Modifier) { + override fun Content(modifier: Modifier) { val state by model.state.collectAsStateWithLifecycle() - TokenList(state = state, contentPadding = contentPadding, modifier = modifier) + TokenList(state = state, modifier = modifier) } @AssistedFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt index cd6a67dbef..6cea4a5b28 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/OnrampTokenListComponent.kt @@ -1,21 +1,16 @@ package com.tangem.features.onramp.tokenlist -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.ui.Modifier import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.onramp.entity.OnrampOperation /** Token list component that present list of token for multi-currency wallet */ @Stable -internal interface OnrampTokenListComponent { - - @Composable - fun Content(contentPadding: PaddingValues, modifier: Modifier) +internal interface OnrampTokenListComponent : ComposableContentComponent { /** Component factory */ interface Factory : ComponentFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt index 55d7d56d1c..a4eed528a1 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/entity/transformer/UpdateTokenItemsTransformer.kt @@ -133,10 +133,7 @@ internal class UpdateTokenItemsTransformer( ) } - private fun createGroupTitle(textReference: TextReference): TokensListItemUM.NetworkGroupTitle { - return TokensListItemUM.NetworkGroupTitle( - id = textReference.hashCode(), - name = textReference, - ) + private fun createGroupTitle(textReference: TextReference): TokensListItemUM.GroupTitle { + return TokensListItemUM.GroupTitle(id = textReference.hashCode(), text = textReference) } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt index eab5741ead..807100bf6a 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/OnrampTokenList.kt @@ -1,16 +1,17 @@ package com.tangem.features.onramp.tokenlist.ui +import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.key 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 androidx.compose.ui.util.fastForEachIndexed import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.tokenlist.TokenListItem import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM @@ -24,34 +25,30 @@ import kotlinx.collections.immutable.ImmutableList /** * Token list * - * @param state state - * @param contentPadding content padding - * @param modifier modifier + * @param state state + * @param modifier modifier * [REDACTED_AUTHOR] */ @Composable -internal fun TokenList(state: TokenListUM, contentPadding: PaddingValues, modifier: Modifier = Modifier) { - LazyColumn(modifier = modifier, contentPadding = contentPadding) { - itemsBlock(items = state.availableItems, isBalanceHidden = state.isBalanceHidden) +internal fun TokenList(state: TokenListUM, modifier: Modifier = Modifier) { + Column(modifier) { + ItemsBlock(items = state.availableItems, isBalanceHidden = state.isBalanceHidden) - item { SpacerH12() } + SpacerH12() - itemsBlock(items = state.unavailableItems, isBalanceHidden = state.isBalanceHidden) + ItemsBlock(items = state.unavailableItems, isBalanceHidden = state.isBalanceHidden) } } -private fun LazyListScope.itemsBlock(items: ImmutableList, isBalanceHidden: Boolean) { - return itemsIndexed( - items = items, - key = { _, item -> item.id }, - contentType = { _, item -> item::class.java }, - itemContent = { index, item -> +@Composable +private fun ItemsBlock(items: ImmutableList, isBalanceHidden: Boolean) { + items.fastForEachIndexed { index, item -> + key(item.id) { TokenListItem( state = item, isBalanceHidden = isBalanceHidden, modifier = Modifier - .animateItem() .roundedShapeItemDecoration( currentIndex = index, lastIndex = items.lastIndex, @@ -59,20 +56,21 @@ private fun LazyListScope.itemsBlock(items: ImmutableList, isB backgroundColor = TangemTheme.colors.background.primary, ), ) - }, - ) + } + } } -@Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun Preview_TokenList(@PreviewParameter(PreviewTokenListUMProvider::class) state: TokenListUM) { TangemThemePreview { TokenList( state = state, - contentPadding = PaddingValues(all = 16.dp), modifier = Modifier .fillMaxWidth() - .background(color = TangemTheme.colors.background.secondary), + .background(color = TangemTheme.colors.background.secondary) + .padding(16.dp), ) } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/preview/PreviewTokenListUMProvider.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/preview/PreviewTokenListUMProvider.kt index 19bb331def..273cad4fd0 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/preview/PreviewTokenListUMProvider.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/tokenlist/ui/preview/PreviewTokenListUMProvider.kt @@ -9,13 +9,24 @@ import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.resourceReference import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.tokenlist.entity.TokenListUM +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList internal class PreviewTokenListUMProvider : PreviewParameterProvider { override val values: Sequence = sequenceOf( - createTokensList(hasSearchBar = false, createDefaultTokenItem()), - createTokensList(hasSearchBar = true, createDefaultTokenItem()), + createTokensList( + hasSearchBar = false, + createHeader(), + createDefaultTokenItem(), + createDefaultTokenItem(), + ), + createTokensList( + hasSearchBar = true, + createHeader(), + createDefaultTokenItem(), + createDefaultTokenItem(), + ), ) private fun createTokensList(hasSearchBar: Boolean, vararg items: TokensListItemUM): TokenListUM { @@ -37,11 +48,22 @@ internal class PreviewTokenListUMProvider : PreviewParameterProvider