Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-18 18:11:05 +04:00
commit fc9db8288d
14 changed files with 651 additions and 84 deletions

View file

@ -15,6 +15,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
@ -23,6 +24,8 @@ import com.tangem.core.ui.res.TangemTheme
@Composable
fun TangemSwitch(
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
checkedColor: Color = TangemTheme.colors.control.checked,
uncheckedColor: Color = TangemTheme.colors.icon.informative,
size: Dp = 48.dp,
@ -39,23 +42,20 @@ fun TangemSwitch(
(if (isChecked) checkedColor else uncheckedColor)
.copy(alpha = if (enabled) 1f else .4f)
}
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = Modifier
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
enabled = enabled,
) {
onCheckedChange(!checked)
}
.indication(
interactionSource = interactionSource,
indication = rememberRipple(
bounded = false,
color = Color.Transparent,
),
enabled = enabled,
role = Role.Switch,
onClick = {
onCheckedChange(!checked)
},
),
) {
BoxWithConstraints(

View file

@ -1,13 +1,17 @@
package com.tangem.core.ui.components.fields
import android.content.res.Configuration
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.onFocusChanged
@ -18,6 +22,7 @@ import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -33,8 +38,9 @@ import com.tangem.core.ui.res.TangemThemePreview
fun SearchBar(state: SearchBarUM, modifier: Modifier = Modifier, colors: TextFieldColors = TangemSearchBarColors) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val interactionSource = remember { MutableInteractionSource() }
TextField(
BasicTextField(
modifier = modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size48)
@ -47,6 +53,7 @@ fun SearchBar(state: SearchBarUM, modifier: Modifier = Modifier, colors: TextFie
},
value = state.query,
onValueChange = state.onQueryChange,
interactionSource = interactionSource,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Search,
@ -59,7 +66,43 @@ fun SearchBar(state: SearchBarUM, modifier: Modifier = Modifier, colors: TextFie
),
singleLine = true,
maxLines = 1,
textStyle = TangemTheme.typography.body2,
textStyle = TangemTheme.typography.body2.copy(
color = TangemTheme.colors.text.primary1,
),
decorationBox = @Composable { innerTextField ->
DecorationBox(
state = state,
innerTextField = innerTextField,
interactionSource = interactionSource,
colors = colors,
focusManager = focusManager,
keyboardController = keyboardController,
)
},
)
}
@Suppress("LongParameterList")
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun DecorationBox(
state: SearchBarUM,
innerTextField: @Composable () -> Unit,
interactionSource: MutableInteractionSource,
colors: TextFieldColors,
focusManager: FocusManager,
keyboardController: SoftwareKeyboardController?,
) {
TextFieldDefaults.DecorationBox(
value = state.query,
innerTextField = innerTextField,
enabled = true,
singleLine = true,
visualTransformation = VisualTransformation.None,
interactionSource = interactionSource,
shape = TangemTheme.shapes.roundedCornersXLarge,
colors = colors,
contentPadding = PaddingValues(all = TangemTheme.dimens.spacing12),
leadingIcon = {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size20),
@ -84,8 +127,6 @@ fun SearchBar(state: SearchBarUM, modifier: Modifier = Modifier, colors: TextFie
overflow = TextOverflow.Ellipsis,
)
},
shape = TangemTheme.shapes.roundedCornersXLarge,
colors = colors,
)
}

View file

@ -29,7 +29,7 @@ fun BlockchainRow(model: BlockchainRowUM, action: @Composable BoxScope.() -> Uni
modifier = modifier
.heightIn(min = TangemTheme.dimens.size52)
.padding(
vertical = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing8,
),
icon = {

View file

@ -2,16 +2,17 @@ package com.tangem.core.ui.components.rows
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
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.R
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.rows.model.ChainRowUM
@ -27,9 +28,10 @@ fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composab
RowContentContainer(
modifier = modifier
.heightIn(min = TangemTheme.dimens.size68)
.padding(vertical = TangemTheme.dimens.spacing8)
.padding(
vertical = TangemTheme.dimens.spacing12,
horizontal = TangemTheme.dimens.spacing14,
start = TangemTheme.dimens.spacing8,
end = TangemTheme.dimens.spacing12,
),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
icon = {
@ -65,17 +67,7 @@ private fun Preview_ChainRow(@PreviewParameter(ChainRowParameterProvider::class)
modifier = Modifier.background(TangemTheme.colors.background.primary),
model = state,
action = {
IconButton(
modifier = Modifier.size(TangemTheme.dimens.size32),
onClick = { /* [REDACTED_TODO_COMMENT]*/ },
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size24),
tint = TangemTheme.colors.icon.informative,
painter = painterResource(id = R.drawable.ic_chevron_24),
contentDescription = null,
)
}
TangemSwitch(onCheckedChange = {}, checked = false)
},
)
}

View file

@ -48,7 +48,6 @@ dependencies {
implementation(deps.tangem.blockchain)
/* AndroidX */
implementation(deps.androidx.fragment.ktx)
implementation(deps.androidx.activity.compose)
implementation(deps.lifecycle.compose)

View file

@ -9,5 +9,10 @@ android {
}
dependencies {
implementation(deps.compose.foundation)
/* Project - Domain */
implementation(projects.domain.wallets.models)
/* Project - Core */
implementation(projects.core.ui)
implementation(projects.core.decompose)
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.managetokens.component
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
interface ManageTokensComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
)
interface Factory : ComponentFactory<Params, ManageTokensComponent>
}

View file

@ -1,6 +1,7 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.serialization)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
@ -11,61 +12,30 @@ android {
}
dependencies {
/** AndroidX */
implementation(deps.androidx.activity.compose)
implementation(deps.material)
/* Project - API */
implementation(projects.features.manageTokens.api)
/** Compose */
implementation(deps.compose.accompanist.systemUiController)
implementation(deps.compose.coil)
implementation(deps.compose.constraintLayout)
implementation(deps.compose.foundation)
implementation(deps.compose.material)
implementation(deps.compose.material3)
implementation(deps.compose.navigation)
implementation(deps.compose.navigation.hilt)
implementation(deps.compose.paging)
implementation(deps.compose.reorderable)
implementation(deps.compose.shimmer)
/* Project - Core */
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.common.routing)
/* AndroidX */
implementation(deps.androidx.activity.compose)
implementation(deps.lifecycle.compose)
/* Compose */
implementation(deps.compose.ui)
implementation(deps.compose.ui.tooling)
implementation(deps.compose.foundation)
implementation(deps.compose.material3)
implementation(deps.compose.shimmer)
/** Other libraries */
implementation(deps.arrow.core)
implementation(deps.jodatime)
implementation(deps.kotlin.immutable.collections)
implementation(deps.tangem.card.core)
implementation(deps.timber)
/** DI */
/* DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
/** Core modules */
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
implementation(projects.core.featuretoggles)
implementation(projects.core.navigation)
implementation(projects.core.ui)
implementation(projects.core.utils)
/** Project - Data */
implementation(projects.data.tokens)
/** Domain modules */
implementation(projects.domain.card)
implementation(projects.domain.demo)
implementation(projects.domain.legacy)
implementation(projects.libs.blockchainSdk)
implementation(projects.domain.models)
implementation(projects.domain.settings)
implementation(projects.domain.tokens)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.appCurrency)
implementation(projects.domain.appCurrency.models)
/** Feature Apis */
implementation(projects.features.manageTokens.api)
/* Other */
implementation(deps.kotlin.immutable.collections)
implementation(deps.timber)
}

View file

@ -0,0 +1,212 @@
package com.tangem.features.managetokens.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.util.fastForEachIndexed
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.rows.model.BlockchainRowUM
import com.tangem.core.ui.components.rows.model.ChainRowUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.managetokens.entity.CurrencyItemUM
import com.tangem.features.managetokens.entity.CurrencyNetworkUM
import com.tangem.features.managetokens.entity.ManageTokensUM
import com.tangem.features.managetokens.impl.R
import com.tangem.features.managetokens.ui.ManageTokensScreen
import kotlinx.collections.immutable.mutate
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
internal class PreviewManageTokensComponent : ManageTokensComponent {
private val changedItemsIds: MutableSet<String> = mutableSetOf()
private var items = initItems()
private val previewState = MutableStateFlow(
value = ManageTokensUM(
popBack = {},
items = items,
search = SearchBarUM(
placeholderText = resourceReference(R.string.manage_tokens_search_placeholder),
query = "",
onQueryChange = ::searchCurrencies,
isActive = false,
onActiveChange = ::toggleSearchBar,
),
hasChanges = false,
onSaveClick = {},
onAddCustomToken = {},
),
)
private fun searchCurrencies(query: String) {
previewState.update { state ->
items = if (query.isBlank()) {
initItems()
} else {
items.filter { currency ->
currency.model.name.contains(query, ignoreCase = true)
}.toPersistentList()
}
state.copy(
search = state.search.copy(query = query),
items = items,
)
}
}
private fun toggleSearchBar(isActive: Boolean) {
previewState.update { state ->
state.copy(
search = state.search.copy(isActive = isActive),
)
}
}
@Composable
override fun Content(modifier: Modifier) {
val state by previewState.collectAsState()
ManageTokensScreen(
modifier = modifier,
state = state,
)
}
private fun initItems() = List(size = 30) { index ->
if (index < 2) {
getCustomItem(index)
} else {
getBasicItem(index)
}
}.toPersistentList()
private fun getCustomItem(index: Int) = CurrencyItemUM.Custom(
id = index.toString(),
model = ChainRowUM(
name = "Custom token $index",
type = "CT$index",
icon = CurrencyIconState.CustomTokenIcon(
tint = Color.White,
background = Color.Black,
topBadgeIconResId = R.drawable.img_eth_22,
isGrayscale = false,
showCustomBadge = true,
),
showCustom = true,
),
onRemoveClick = {},
)
private fun getBasicItem(index: Int) = CurrencyItemUM.Basic(
id = index.toString(),
model = ChainRowUM(
name = "Currency $index",
type = "C$index",
icon = CurrencyIconState.CoinIcon(
url = null,
fallbackResId = R.drawable.img_btc_22,
isGrayscale = false,
showCustomBadge = false,
),
showCustom = false,
),
networks = if (index == 2) {
CurrencyItemUM.Basic.NetworksUM.Expanded(getCurrencyNetworks(index))
} else {
CurrencyItemUM.Basic.NetworksUM.Collapsed
},
onExpandClick = { toggleCurrency(index) },
)
private fun getCurrencyNetworks(currencyIndex: Int) = List(size = 3) { networkIndex ->
CurrencyNetworkUM(
id = networkIndex.toString(),
model = BlockchainRowUM(
name = "NETWORK$networkIndex",
type = "N$networkIndex",
iconResId = R.drawable.ic_eth_16,
isMainNetwork = networkIndex == 0,
isSelected = false,
),
isSelected = false,
onSelectedStateChange = { toggleNetwork(currencyIndex, networkIndex, isSelected = it) },
)
}.toImmutableList()
private fun toggleCurrency(index: Int) {
val updatedItem = when (val item = items[index]) {
is CurrencyItemUM.Basic -> item.copy(
networks = if (item.networks is CurrencyItemUM.Basic.NetworksUM.Collapsed) {
CurrencyItemUM.Basic.NetworksUM.Expanded(getCurrencyNetworks(index))
} else {
CurrencyItemUM.Basic.NetworksUM.Collapsed
},
)
is CurrencyItemUM.Custom -> return
}
previewState.update { state ->
items = items.mutate {
it[index] = updatedItem
}
state.copy(items = items)
}
}
private fun toggleNetwork(currencyIndex: Int, networkIndex: Int, isSelected: Boolean) {
val updatedItem = when (val item = items[currencyIndex]) {
is CurrencyItemUM.Basic -> {
val updatedNetworks = (item.networks as? CurrencyItemUM.Basic.NetworksUM.Expanded)
?.copy(
networks = item.networks.networks.toPersistentList().mutate {
it.fastForEachIndexed { index, network ->
if (index == networkIndex) {
it[index] = network.copy(
model = network.model.copy(
iconResId = if (isSelected) {
R.drawable.img_eth_22
} else {
R.drawable.ic_eth_16
},
isSelected = isSelected,
),
isSelected = isSelected,
)
}
}
},
)
?: return
item.copy(networks = updatedNetworks)
}
is CurrencyItemUM.Custom -> return
}
val id = "${currencyIndex}_$networkIndex"
if (changedItemsIds.contains(id)) {
changedItemsIds.remove(id)
} else {
changedItemsIds.add(id)
}
previewState.update { state ->
items = items.mutate {
it[currencyIndex] = updatedItem
}
state.copy(
items = items,
hasChanges = changedItemsIds.isNotEmpty(),
)
}
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.features.managetokens.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.rows.model.ChainRowUM
import kotlinx.collections.immutable.ImmutableList
@Immutable
internal sealed class CurrencyItemUM {
abstract val id: String
abstract val model: ChainRowUM
data class Basic(
override val id: String,
override val model: ChainRowUM,
val networks: NetworksUM,
val onExpandClick: () -> Unit,
) : CurrencyItemUM() {
@Immutable
sealed class NetworksUM {
data object Collapsed : NetworksUM()
data class Expanded(
val networks: ImmutableList<CurrencyNetworkUM>,
) : NetworksUM()
}
}
data class Custom(
override val id: String,
override val model: ChainRowUM,
val onRemoveClick: () -> Unit,
) : CurrencyItemUM()
}

View file

@ -0,0 +1,12 @@
package com.tangem.features.managetokens.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.rows.model.BlockchainRowUM
@Immutable
internal data class CurrencyNetworkUM(
val id: String,
val model: BlockchainRowUM,
val isSelected: Boolean,
val onSelectedStateChange: (Boolean) -> Unit,
)

View file

@ -0,0 +1,15 @@
package com.tangem.features.managetokens.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import kotlinx.collections.immutable.ImmutableList
@Immutable
internal data class ManageTokensUM(
val popBack: () -> Unit,
val items: ImmutableList<CurrencyItemUM>,
val search: SearchBarUM,
val hasChanges: Boolean,
val onAddCustomToken: () -> Unit,
val onSaveClick: () -> Unit,
)

View file

@ -0,0 +1,272 @@
package com.tangem.features.managetokens.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.animation.*
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.util.fastForEachIndexed
import com.tangem.core.ui.components.BottomFade
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.buttons.SecondarySmallButton
import com.tangem.core.ui.components.buttons.SmallButtonConfig
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.rows.ArrowRow
import com.tangem.core.ui.components.rows.BlockchainRow
import com.tangem.core.ui.components.rows.ChainRow
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.managetokens.component.preview.PreviewManageTokensComponent
import com.tangem.features.managetokens.entity.CurrencyItemUM
import com.tangem.features.managetokens.entity.CurrencyItemUM.Basic.NetworksUM
import com.tangem.features.managetokens.entity.ManageTokensUM
import com.tangem.features.managetokens.impl.R
import kotlinx.collections.immutable.ImmutableList
private const val CHEVRON_ROTATION_EXPANDED = 180f
private const val CHEVRON_ROTATION_COLLAPSED = 0f
@Composable
internal fun ManageTokensScreen(state: ManageTokensUM, modifier: Modifier = Modifier) {
BackHandler(onBack = state.popBack)
Scaffold(
modifier = modifier,
containerColor = TangemTheme.colors.background.primary,
topBar = {
TangemTopAppBar(
modifier = Modifier.statusBarsPadding(),
title = stringResource(id = R.string.main_manage_tokens),
startButton = TopAppBarButtonUM.Back(state.popBack),
endButton = TopAppBarButtonUM(
iconRes = R.drawable.ic_plus_24,
onIconClicked = state.onAddCustomToken,
),
)
},
content = { innerPadding ->
Content(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
state = state,
)
},
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
SaveChangesButton(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
isVisible = state.hasChanges,
onClick = state.onSaveClick,
)
},
)
}
@Composable
private fun SaveChangesButton(isVisible: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
AnimatedVisibility(
modifier = modifier,
visible = isVisible,
enter = fadeIn(),
exit = fadeOut(),
label = "save_button_visibility",
) {
PrimaryButtonIconEnd(
text = stringResource(id = R.string.common_save),
iconResId = R.drawable.ic_tangem_24,
onClick = onClick,
)
}
}
@Composable
private fun Content(state: ManageTokensUM, modifier: Modifier = Modifier) {
Box(modifier = modifier) {
Currencies(
modifier = Modifier.fillMaxSize(),
items = state.items,
search = state.search,
)
AnimatedVisibility(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth(),
visible = state.hasChanges,
label = "bottom_fade_visibility",
) {
BottomFade()
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun Currencies(items: ImmutableList<CurrencyItemUM>, search: SearchBarUM, modifier: Modifier = Modifier) {
LazyColumn(
modifier = modifier,
) {
stickyHeader(key = "search") {
Column(
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.padding(
top = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing12,
)
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
) {
SearchBar(state = search)
}
}
items(
items = items,
key = CurrencyItemUM::id,
) { item ->
when (item) {
is CurrencyItemUM.Basic -> {
BasicCurrencyItem(
modifier = Modifier.fillMaxWidth(),
item = item,
)
}
is CurrencyItemUM.Custom -> {
CustomCurrencyItem(
modifier = Modifier.fillMaxWidth(),
item = item,
)
}
}
}
}
}
@Composable
private fun CustomCurrencyItem(item: CurrencyItemUM.Custom, modifier: Modifier = Modifier) {
ChainRow(
modifier = modifier,
model = item.model,
action = {
SecondarySmallButton(
config = SmallButtonConfig(
text = resourceReference(R.string.manage_tokens_remove),
onClick = item.onRemoveClick,
),
)
},
)
}
@Composable
private fun BasicCurrencyItem(item: CurrencyItemUM.Basic, modifier: Modifier = Modifier) {
val isExpanded = item.networks is NetworksUM.Expanded
Column(modifier = modifier) {
ChainRow(
modifier = Modifier.clickable(onClick = item.onExpandClick),
model = item.model,
action = {
val rotation by animateFloatAsState(
targetValue = if (isExpanded) {
CHEVRON_ROTATION_EXPANDED
} else {
CHEVRON_ROTATION_COLLAPSED
},
label = "chevron_rotation",
)
Icon(
modifier = Modifier
.rotate(rotation)
.size(TangemTheme.dimens.size24),
painter = painterResource(id = R.drawable.ic_chevron_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
},
)
NetworksList(
modifier = Modifier.padding(
start = TangemTheme.dimens.spacing10,
end = TangemTheme.dimens.spacing8,
),
networks = item.networks,
currencyId = item.id,
)
}
}
@Composable
private fun NetworksList(networks: NetworksUM, currencyId: String, modifier: Modifier = Modifier) {
AnimatedVisibility(
modifier = modifier,
visible = networks is NetworksUM.Expanded,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically(),
label = "networks_visibility",
) {
Column {
val items = (networks as? NetworksUM.Expanded)?.networks
// To keep items on collapse and avoid animation cancellation
val rememberedItems = remember(key1 = currencyId) { items }
val currentItems = items ?: rememberedItems
currentItems?.fastForEachIndexed { index, network ->
ArrowRow(
isLastItem = index == currentItems.lastIndex,
content = {
BlockchainRow(
model = network.model,
action = {
TangemSwitch(
checked = network.isSelected,
onCheckedChange = network.onSelectedStateChange,
)
},
)
},
)
}
}
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360, heightDp = 800)
@Preview(showBackground = true, widthDp = 360, heightDp = 800, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview_ManageTokens() {
TangemThemePreview {
PreviewManageTokensComponent().Content(Modifier.fillMaxWidth())
}
}
// endregion Preview

View file

@ -24,7 +24,6 @@ internal fun BaseExtension.configureCompose(project: Project) {
contains(":features:onboarding") || // TODO: divide on api/impl after migrating all onboarding to module
contains(Regex(pattern = ":presentation\$")) ||
contains(Regex(pattern = ":app\$")) || // TODO: [REDACTED_JIRA]
contains(Regex(pattern = ":features:manage-tokens:api\$")) || // provides Composable function
contains(Regex(pattern = ":features:markets:api\$")) || // provides Composable function
contains(Regex(pattern = ":impl\$"))
}