Updated on 2026-08-14
This commit is contained in:
parent
a246f92ea7
commit
e51c82e615
5 changed files with 262 additions and 60 deletions
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.common.ui.userwallet
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -26,6 +29,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.coil.RotationTransformation
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -33,9 +37,14 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
fun UserWalletItem(state: UserWalletItemUM, modifier: Modifier = Modifier) {
|
||||
fun UserWalletItem(
|
||||
state: UserWalletItemUM,
|
||||
modifier: Modifier = Modifier,
|
||||
blockColors: CardColors = TangemBlockCardColors,
|
||||
) {
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = blockColors,
|
||||
onClick = state.onClick,
|
||||
enabled = state.isEnabled,
|
||||
) {
|
||||
|
|
@ -142,6 +151,7 @@ private fun CardImage(imageUrl: String, modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
|
|
@ -174,7 +184,12 @@ private fun Preview() {
|
|||
),
|
||||
)
|
||||
|
||||
Column {
|
||||
Column(
|
||||
Modifier
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
list.fastForEach { userWalletItemUM ->
|
||||
UserWalletItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
package com.tangem.core.ui.components.buttons.common
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -36,6 +41,7 @@ fun TangemButton(
|
|||
textStyle: TextStyle = TangemTheme.typography.button,
|
||||
shape: Shape = size.toShape(),
|
||||
iconPadding: Dp = size.toIconPadding(),
|
||||
animateContentChange: Boolean = false,
|
||||
) {
|
||||
val multipleClickPreventer = remember { MultipleClickPreventer.get() }
|
||||
|
||||
|
|
@ -56,6 +62,7 @@ fun TangemButton(
|
|||
buttonIcon = icon,
|
||||
iconPadding = iconPadding,
|
||||
showProgress = showProgress,
|
||||
animateContentChange = animateContentChange,
|
||||
progressIndicator = {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.buttonContentSize(maxContentSize),
|
||||
|
|
@ -108,24 +115,54 @@ private inline fun RowScope.ButtonContentContainer(
|
|||
buttonIcon: TangemButtonIconPosition,
|
||||
iconPadding: Dp,
|
||||
showProgress: Boolean,
|
||||
animateContentChange: Boolean,
|
||||
progressIndicator: @Composable RowScope.() -> Unit,
|
||||
text: @Composable RowScope.() -> Unit,
|
||||
icon: @Composable RowScope.(Int) -> Unit,
|
||||
crossinline text: @Composable RowScope.() -> Unit,
|
||||
crossinline icon: @Composable RowScope.(Int) -> Unit,
|
||||
additionalText: @Composable () -> Unit,
|
||||
) {
|
||||
if (showProgress) {
|
||||
progressIndicator()
|
||||
} else {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Row(horizontalArrangement = Arrangement.Center) {
|
||||
if (buttonIcon is TangemButtonIconPosition.Start) {
|
||||
icon(buttonIcon.iconResId)
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
if (animateContentChange) {
|
||||
AnimatedContent(
|
||||
targetState = buttonIcon,
|
||||
transitionSpec = {
|
||||
fadeIn(tween(durationMillis = 220)) togetherWith
|
||||
fadeOut(tween(durationMillis = 220))
|
||||
},
|
||||
label = "button text with icon",
|
||||
) { iconState ->
|
||||
Row(horizontalArrangement = Arrangement.Center) {
|
||||
when (iconState) {
|
||||
is TangemButtonIconPosition.Start -> {
|
||||
icon(iconState.iconResId)
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
text()
|
||||
}
|
||||
is TangemButtonIconPosition.End -> {
|
||||
text()
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
icon(iconState.iconResId)
|
||||
}
|
||||
is TangemButtonIconPosition.None -> {
|
||||
text()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text()
|
||||
if (buttonIcon is TangemButtonIconPosition.End) {
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
icon(buttonIcon.iconResId)
|
||||
} else {
|
||||
Row(horizontalArrangement = Arrangement.Center) {
|
||||
if (buttonIcon is TangemButtonIconPosition.Start) {
|
||||
icon(buttonIcon.iconResId)
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
}
|
||||
text()
|
||||
if (buttonIcon is TangemButtonIconPosition.End) {
|
||||
Spacer(modifier = Modifier.requiredWidth(iconPadding))
|
||||
icon(buttonIcon.iconResId)
|
||||
}
|
||||
}
|
||||
}
|
||||
additionalText()
|
||||
|
|
|
|||
|
|
@ -2,29 +2,36 @@ package com.tangem.features.markets.portfolio.impl.ui
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.common.ui.userwallet.UserWalletItem
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.components.SpacerW6
|
||||
import com.tangem.core.ui.components.TangemSwitch
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.components.block.information.InformationBlock
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButton
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.components.currency.icon.CoinIcon
|
||||
import com.tangem.core.ui.components.rows.ArrowRow
|
||||
import com.tangem.core.ui.components.rows.BlockchainRow
|
||||
|
|
@ -36,18 +43,18 @@ import com.tangem.features.markets.impl.R
|
|||
import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewAddToPortfolioBSContentProvider
|
||||
import com.tangem.features.markets.portfolio.impl.ui.state.AddToPortfolioBSContentUM
|
||||
import com.tangem.features.markets.portfolio.impl.ui.state.SelectNetworkUM
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
internal fun AddToPortfolioBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet<AddToPortfolioBSContentUM>(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
addBottomInsets = false,
|
||||
titleText = resourceReference(R.string.markets_add_to_portfolio_button),
|
||||
) {
|
||||
Content(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = TangemTheme.dimens.spacing16),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = config.content as AddToPortfolioBSContentUM,
|
||||
)
|
||||
}
|
||||
|
|
@ -55,33 +62,101 @@ internal fun AddToPortfolioBottomSheet(config: TangemBottomSheetConfig) {
|
|||
|
||||
@Composable
|
||||
private fun Content(state: AddToPortfolioBSContentUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
UserWalletItem(state.selectedWallet)
|
||||
var continueButtonAreaHeight by remember { mutableIntStateOf(0) }
|
||||
val density = LocalDensity.current
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
NetworkSelection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = state.selectNetworkUM,
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = state.isScanCardNotificationVisible,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
Box(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = scrollState)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
ScanWalletWarning(modifier = Modifier.fillMaxWidth())
|
||||
UserWalletItem(
|
||||
state = state.selectedWallet,
|
||||
blockColors = TangemBlockCardColors.copy(
|
||||
containerColor = TangemTheme.colors.background.action,
|
||||
disabledContainerColor = TangemTheme.colors.background.action,
|
||||
),
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
||||
NetworkSelection(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = state.selectNetworkUM,
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = state.isScanCardNotificationVisible,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
ScanWalletWarning(modifier = Modifier.fillMaxWidth())
|
||||
SpacerH12()
|
||||
}
|
||||
|
||||
// Scroll to the bottom when the notification appears and the scroll is at the bottom
|
||||
LaunchedEffect(Unit) {
|
||||
if (scrollState.canScrollForward.not()) {
|
||||
delay(timeMillis = 500)
|
||||
scrollState.animateScrollTo(scrollState.maxValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH(with(density) { continueButtonAreaHeight.toDp() })
|
||||
}
|
||||
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.common_continue),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
onClick = {},
|
||||
AnimatedVisibility(
|
||||
visible = scrollState.canScrollForward,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
) {
|
||||
BottomFade(Modifier.align(Alignment.BottomCenter))
|
||||
}
|
||||
|
||||
ContinueButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.onGloballyPositioned {
|
||||
continueButtonAreaHeight = it.size.height
|
||||
},
|
||||
enabled = state.continueButtonEnabled,
|
||||
onClick = state.onContinueButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContinueButton(enabled: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
TangemButton(
|
||||
enabled = enabled,
|
||||
modifier = modifier
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.navigationBarsPadding()
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(R.string.common_continue),
|
||||
icon = if (enabled) {
|
||||
TangemButtonIconPosition.End(R.drawable.ic_tangem_24)
|
||||
} else {
|
||||
TangemButtonIconPosition.None
|
||||
},
|
||||
showProgress = false,
|
||||
size = TangemButtonSize.Default,
|
||||
colors = TangemButtonsDefaults.primaryButtonColors,
|
||||
onClick = onClick,
|
||||
animateContentChange = true,
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifier) {
|
||||
|
|
@ -95,10 +170,7 @@ private fun NetworkSelection(state: SelectNetworkUM, modifier: Modifier = Modifi
|
|||
)
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -180,6 +252,7 @@ private fun ScanWalletWarning(modifier: Modifier = Modifier) {
|
|||
Icon(
|
||||
modifier = Modifier.requiredSize(TangemTheme.dimens.size20),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_tangem_24),
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
|
|
@ -237,4 +310,53 @@ private fun PreviewContentRtl(
|
|||
state = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// For on device testing
|
||||
@Composable
|
||||
@Preview
|
||||
private fun PreviewContentTestOnDevice(
|
||||
@PreviewParameter(PreviewAddToPortfolioBSContentProvider::class) content: AddToPortfolioBSContentUM,
|
||||
) {
|
||||
TangemThemePreview(
|
||||
alwaysShowBottomSheets = false,
|
||||
) {
|
||||
var isShow by remember { mutableStateOf(false) }
|
||||
|
||||
var contentState by remember {
|
||||
mutableStateOf(content)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
contentState = content.copy(
|
||||
onContinueButtonClick = {
|
||||
contentState = contentState.copy(
|
||||
isScanCardNotificationVisible = !contentState.isScanCardNotificationVisible,
|
||||
)
|
||||
},
|
||||
continueButtonEnabled = true,
|
||||
selectedWallet = content.selectedWallet.copy(
|
||||
onClick = {
|
||||
contentState = contentState.copy(
|
||||
continueButtonEnabled = !contentState.continueButtonEnabled,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
AddToPortfolioBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShow = isShow,
|
||||
content = contentState,
|
||||
onDismissRequest = { isShow = false },
|
||||
),
|
||||
)
|
||||
|
||||
Button(
|
||||
onClick = { isShow = !isShow },
|
||||
) {
|
||||
Text(text = "Toggle")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,14 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
|
||||
internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider<AddToPortfolioBSContentUM> {
|
||||
|
||||
private val blockchainRow = BlockchainRowUM(
|
||||
name = "Etherium 3",
|
||||
type = "TEST",
|
||||
iconResId = R.drawable.ic_eth_16,
|
||||
isMainNetwork = false,
|
||||
isSelected = false,
|
||||
)
|
||||
|
||||
override val values: Sequence<AddToPortfolioBSContentUM>
|
||||
get() = sequenceOf(
|
||||
AddToPortfolioBSContentUM(
|
||||
|
|
@ -29,32 +37,50 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider
|
|||
tokenName = "Etherium",
|
||||
tokenCurrencySymbol = "ETH",
|
||||
networks = persistentListOf(
|
||||
BlockchainRowUM(
|
||||
name = "Etherium",
|
||||
blockchainRow.copy(
|
||||
type = "MAIN",
|
||||
iconResId = R.drawable.ic_eth_16,
|
||||
isMainNetwork = true,
|
||||
isSelected = true,
|
||||
),
|
||||
BlockchainRowUM(
|
||||
name = "Etherium 2",
|
||||
type = "TEST",
|
||||
iconResId = R.drawable.ic_eth_16,
|
||||
isMainNetwork = false,
|
||||
isSelected = false,
|
||||
),
|
||||
BlockchainRowUM(
|
||||
name = "Etherium 3",
|
||||
type = "TEST",
|
||||
iconResId = R.drawable.ic_eth_16,
|
||||
isMainNetwork = false,
|
||||
isSelected = false,
|
||||
),
|
||||
blockchainRow,
|
||||
blockchainRow,
|
||||
),
|
||||
onNetworkSwitchClick = { _, _ -> },
|
||||
iconUrl = null,
|
||||
),
|
||||
isScanCardNotificationVisible = true,
|
||||
continueButtonEnabled = true,
|
||||
onContinueButtonClick = {},
|
||||
),
|
||||
AddToPortfolioBSContentUM(
|
||||
selectedWallet = UserWalletItemUM(
|
||||
id = UserWalletId("1"),
|
||||
name = stringReference("Wallet 1"),
|
||||
information = stringReference("3 cards, 10,123$"),
|
||||
imageUrl = "",
|
||||
isEnabled = true,
|
||||
endIcon = UserWalletItemUM.EndIcon.Arrow,
|
||||
onClick = {},
|
||||
),
|
||||
selectNetworkUM = SelectNetworkUM(
|
||||
tokenId = "etherium",
|
||||
tokenName = "Etherium",
|
||||
tokenCurrencySymbol = "ETH",
|
||||
networks = persistentListOf(
|
||||
blockchainRow.copy(
|
||||
type = "MAIN",
|
||||
isMainNetwork = true,
|
||||
isSelected = true,
|
||||
),
|
||||
*Array(25) { blockchainRow },
|
||||
),
|
||||
|
||||
onNetworkSwitchClick = { _, _ -> },
|
||||
iconUrl = null,
|
||||
),
|
||||
isScanCardNotificationVisible = true,
|
||||
continueButtonEnabled = false,
|
||||
onContinueButtonClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -7,4 +7,6 @@ internal data class AddToPortfolioBSContentUM(
|
|||
val selectedWallet: UserWalletItemUM,
|
||||
val selectNetworkUM: SelectNetworkUM,
|
||||
val isScanCardNotificationVisible: Boolean,
|
||||
val continueButtonEnabled: Boolean,
|
||||
val onContinueButtonClick: () -> Unit,
|
||||
) : TangemBottomSheetConfigContent
|
||||
Loading…
Add table
Add a link
Reference in a new issue