Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-30 21:18:17 +05:00
parent e2af488b32
commit fdbd09cccb
20 changed files with 852 additions and 77 deletions

View file

@ -32,6 +32,7 @@ dependencies {
implementation(projects.features.txhistory.api)
implementation(projects.features.tokendetails.api)
implementation(projects.features.promoBanners.api)
implementation(projects.features.virtualAccounts.details.api) // TWI_1638_VA_MVP0_ENABLED
/** Domain */
implementation(projects.domain.balanceHiding)

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.TangemPayTopUpData
@ -39,11 +40,13 @@ internal class TangemPayAddFundsComponent(
val fiatBalance: BigDecimal,
val depositAddress: String,
val cryptoCurrency: CryptoCurrency,
val virtualAccountOnramp: VirtualAccountOnramp?,
)
}
internal interface AddFundsListener {
fun onClickReceive(data: TangemPayTopUpData)
fun onClickSwap(data: TangemPayTopUpData)
fun onClickBankTransfer()
fun onDismissAddFunds()
}

View file

@ -97,6 +97,14 @@ internal class TangemPayCardPageScreenComponent(
fiatBalance = navigation.fiatBalance,
depositAddress = navigation.depositAddress,
cryptoCurrency = navigation.cryptoCurrency,
virtualAccountOnramp = navigation.virtualAccountOnramp,
),
)
is TangemPayCardNavigation.VirtualAccountDeposit -> TangemPayVirtualAccountDepositComponent(
appComponentContext = context,
params = TangemPayVirtualAccountDepositComponent.Params(
virtualAccountOnramp = navigation.virtualAccountOnramp,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
is TangemPayCardNavigation.Receive -> tokenReceiveComponentFactory.create(

View file

@ -143,6 +143,14 @@ internal class TangemPayDetailsComponent(
depositAddress = navigation.depositAddress,
cryptoCurrency = navigation.cryptoCurrency,
listener = model,
virtualAccountOnramp = navigation.virtualAccountOnramp,
),
)
is TangemPayDetailsNavigation.VirtualAccountDeposit -> TangemPayVirtualAccountDepositComponent(
appComponentContext = context,
params = TangemPayVirtualAccountDepositComponent.Params(
virtualAccountOnramp = navigation.virtualAccountOnramp,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
is TangemPayDetailsNavigation.IssueAdditionalCard -> TangemPayIssueAdditionalCardComponent(

View file

@ -0,0 +1,35 @@
package com.tangem.features.tangempay.components
import androidx.compose.runtime.Composable
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.features.tangempay.model.TangemPayVirtualAccountDepositModel
import com.tangem.features.tangempay.ui.TangemPayVirtualAccountDepositBottomSheet
/**
* Bank-transfer deposit bottom sheet (VA MVP0, TWI-1638). Opened from the add-funds "Bank transfer" option.
* Renders the on-ramp intro; the [VirtualAccountOnramp.Eligible] state additionally shows a T&C consent footer.
*/
internal class TangemPayVirtualAccountDepositComponent(
appComponentContext: AppComponentContext,
params: Params,
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
private val model: TangemPayVirtualAccountDepositModel = getOrCreateModel(params = params)
override fun dismiss() {
model.onDismiss()
}
@Composable
override fun BottomSheet() {
TangemPayVirtualAccountDepositBottomSheet(state = model.uiState)
}
data class Params(
val virtualAccountOnramp: VirtualAccountOnramp,
val onDismiss: () -> Unit,
)
}

View file

@ -46,6 +46,11 @@ internal interface TangemPayModelModule {
@ClassKey(TangemPayAddFundsModel::class)
fun bindTangemPayAddFundsModel(model: TangemPayAddFundsModel): Model
@Binds
@IntoMap
@ClassKey(TangemPayVirtualAccountDepositModel::class)
fun bindTangemPayVirtualAccountDepositModel(model: TangemPayVirtualAccountDepositModel): Model
@Binds
@IntoMap
@ClassKey(TangemPayViewPinModel::class)

View file

@ -1,6 +1,7 @@
package com.tangem.features.tangempay.entity
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.wallet.UserWalletId
@ -30,6 +31,12 @@ internal sealed class TangemPayCardNavigation {
val fiatBalance: SerializedBigDecimal,
val depositAddress: String,
val cryptoCurrency: CryptoCurrency,
val virtualAccountOnramp: VirtualAccountOnramp?,
) : TangemPayCardNavigation()
@Serializable
data class VirtualAccountDeposit(
val virtualAccountOnramp: VirtualAccountOnramp,
) : TangemPayCardNavigation()
@Serializable

View file

@ -1,6 +1,7 @@
package com.tangem.features.tangempay.entity
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.serialization.SerializedBigDecimal
import com.tangem.domain.models.serialization.SerializedCurrency
@ -21,6 +22,12 @@ internal sealed class TangemPayDetailsNavigation {
val fiatBalance: SerializedBigDecimal,
val depositAddress: String,
val cryptoCurrency: CryptoCurrency,
val virtualAccountOnramp: VirtualAccountOnramp?,
) : TangemPayDetailsNavigation()
@Serializable
data class VirtualAccountDeposit(
val virtualAccountOnramp: VirtualAccountOnramp,
) : TangemPayDetailsNavigation()
@Serializable

View file

@ -0,0 +1,27 @@
package com.tangem.features.tangempay.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
/**
* UI state of the bank-transfer deposit bottom sheet (VA MVP0, TWI-1638).
*
* @property shouldShowTermsAndConditions `true` for the `Eligible` state shows the provider T&C consent footer.
*/
@Immutable
internal data class TangemPayVirtualAccountDepositUM(
val fees: ImmutableList<FeeRow>,
val shouldShowTermsAndConditions: Boolean,
val onShowDetailsClick: () -> Unit,
val onDismiss: () -> Unit,
val onTermsClick: () -> Unit,
val onPrivacyClick: () -> Unit,
) {
@Immutable
data class FeeRow(
val title: TextReference,
val value: String,
)
}

View file

@ -11,6 +11,7 @@ import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
import com.tangem.features.virtualaccount.VirtualAccountFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
@ -20,6 +21,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
private val virtualAccountToggles: VirtualAccountFeatureToggles,
) : Model() {
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
@ -43,6 +45,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
return TangemPayAddFundsUMConverter(
listener = params.listener,
isRedesignEnabled = tangemPayFeatureToggles.isRedesignEnabled,
shouldShowBankTransfer = virtualAccountToggles.isVaMvp0Enabled && params.virtualAccountOnramp != null,
).convert(data)
}

View file

@ -449,6 +449,7 @@ internal class TangemPayCardPageModel @Inject constructor(
cryptoBalance = balance.cryptoBalance.balance,
depositAddress = balance.cryptoBalance.depositAddress,
cryptoCurrency = cryptoCurrency,
virtualAccountOnramp = currentStatus.value.ifLoadedOrNull { it.virtualAccount },
),
)
}
@ -484,6 +485,12 @@ internal class TangemPayCardPageModel @Inject constructor(
)
}
override fun onClickBankTransfer() {
val onramp = currentStatus.value.ifLoadedOrNull { it.virtualAccount } ?: return
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(TangemPayCardNavigation.VirtualAccountDeposit(onramp))
}
override fun onDismissAddFunds() {
bottomSheetNavigation.dismiss()
}

View file

@ -210,6 +210,7 @@ internal class TangemPayDetailsModel @Inject constructor(
cryptoBalance = balance.availableForWithdrawal,
depositAddress = balance.cryptoBalance.depositAddress,
cryptoCurrency = cryptoCurrency,
virtualAccountOnramp = currentStatus.value.ifLoadedOrNull { it.virtualAccount },
),
)
}
@ -344,6 +345,12 @@ internal class TangemPayDetailsModel @Inject constructor(
)
}
override fun onClickBankTransfer() {
val onramp = currentStatus.value.ifLoadedOrNull { it.virtualAccount } ?: return
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(TangemPayDetailsNavigation.VirtualAccountDeposit(onramp))
}
override fun onClickReceive(data: TangemPayTopUpData) {
analytics.send(TangemPayAnalyticsEvents.ReceiveFundsClicked())
bottomSheetNavigation.dismiss()

View file

@ -0,0 +1,50 @@
package com.tangem.features.tangempay.model
import androidx.compose.runtime.Stable
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.account.VirtualAccountOnramp
import com.tangem.features.tangempay.components.TangemPayVirtualAccountDepositComponent
import com.tangem.features.tangempay.entity.TangemPayVirtualAccountDepositUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import javax.inject.Inject
@Stable
@ModelScoped
internal class TangemPayVirtualAccountDepositModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val urlOpener: UrlOpener,
) : Model() {
private val params = paramsContainer.require<TangemPayVirtualAccountDepositComponent.Params>()
val uiState: TangemPayVirtualAccountDepositUM = TangemPayVirtualAccountDepositUM(
fees = persistentListOf(
TangemPayVirtualAccountDepositUM.FeeRow(title = stringReference("ACH"), value = "$1"),
TangemPayVirtualAccountDepositUM.FeeRow(title = stringReference("FedWire"), value = "$11"),
),
shouldShowTermsAndConditions = params.virtualAccountOnramp is VirtualAccountOnramp.Eligible,
onShowDetailsClick = ::onShowDetailsClick,
onDismiss = ::onDismiss,
onTermsClick = { urlOpener.openUrl(TERMS_OF_USE_URL) },
onPrivacyClick = { urlOpener.openUrl(PRIVACY_POLICY_URL) },
)
fun onDismiss() {
params.onDismiss()
}
private fun onShowDetailsClick() {
// TODO([REDACTED_TASK_KEY]): VA MVP0 — open the bank-transfer requisites screen (separate PR).
}
private companion object {
const val TERMS_OF_USE_URL = "https://tangem.com/docs/en/virtual-account-terms.pdf"
const val PRIVACY_POLICY_URL = "https://tangem.com/docs/en/pay-privacy-policy.pdf"
}
}

View file

@ -1,82 +1,89 @@
package com.tangem.features.tangempay.model.transformers
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_card_20
import com.tangem.core.ui.res.generated.icons.ic_logo_tangem_20
import com.tangem.core.ui.res.generated.icons.ic_sign_usd_20
import com.tangem.domain.pay.model.TangemPayTopUpData
import com.tangem.features.tangempay.components.AddFundsListener
import com.tangem.features.tangempay.details.impl.R
import com.tangem.features.tangempay.entity.TangemPayAddFundsItemUM
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
import com.tangem.features.tangempay.entity.TangemPayDetailsErrorType
import com.tangem.features.tangempay.utils.TangemPayMessagesFactory
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.persistentListOf
import com.tangem.utils.extensions.addIf
import kotlinx.collections.immutable.toPersistentList
internal class TangemPayAddFundsUMConverter(
val listener: AddFundsListener,
val isRedesignEnabled: Boolean,
) : Converter<TangemPayTopUpData?, TangemPayAddFundsUM> {
val shouldShowBankTransfer: Boolean,
) : Converter<TangemPayTopUpData, TangemPayAddFundsUM> {
override fun convert(value: TangemPayTopUpData?): TangemPayAddFundsUM {
return if (value == null) {
TangemPayAddFundsUM(
items = persistentListOf(),
dismiss = listener::onDismissAddFunds,
errorMessage = TangemPayMessagesFactory.createErrorMessage(
errorType = TangemPayDetailsErrorType.Receive,
).messageBottomSheetUM,
)
} else {
TangemPayAddFundsUM(
items = persistentListOf(
TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) {
TangemIconUM.Icon(
imageVector = Icons.ic_logo_tangem_20,
tintReference = {
TangemTheme.colors3.icon.brand
},
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_exchange_vertical_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = TextReference.Res(R.string.tangempay_topup_swap_title),
description = TextReference.Res(R.string.tangempay_topup_swap_body),
onClick = { listener.onClickSwap(value) },
),
TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) {
TangemIconUM.Icon(
imageVector = Icons.ic_card_20,
tintReference = {
TangemTheme.colors3.icon.brand
},
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_arrow_down_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = TextReference.Res(R.string.tangempay_topup_receive_title),
description = TextReference.Res(R.string.tangempay_topup_receive_body),
onClick = { listener.onClickReceive(value) },
),
),
dismiss = listener::onDismissAddFunds,
errorMessage = null,
)
}
@Suppress("UnnecessaryLet")
override fun convert(value: TangemPayTopUpData): TangemPayAddFundsUM {
return TangemPayAddFundsUM(
items = buildList {
TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) {
TangemIconUM.Icon(
imageVector = Icons.ic_logo_tangem_20,
tintReference = {
TangemTheme.colors3.icon.brand
},
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_exchange_vertical_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = resourceReference(R.string.tangempay_topup_swap_title),
description = resourceReference(R.string.tangempay_topup_swap_body),
onClick = { listener.onClickSwap(value) },
).let(::add)
TangemPayAddFundsItemUM(
icon = if (isRedesignEnabled) {
TangemIconUM.Icon(
imageVector = Icons.ic_card_20,
tintReference = {
TangemTheme.colors3.icon.brand
},
)
} else {
TangemIconUM.Icon(
iconRes = R.drawable.ic_arrow_down_24,
tintReference = {
TangemTheme.colors.icon.accent
},
)
},
title = resourceReference(R.string.tangempay_topup_receive_title),
description = resourceReference(R.string.tangempay_topup_receive_body),
onClick = { listener.onClickReceive(value) },
).let(::add)
addIf(
condition = shouldShowBankTransfer,
create = {
TangemPayAddFundsItemUM(
icon = TangemIconUM.Icon(
imageVector = Icons.ic_sign_usd_20,
tintReference = { TangemTheme.colors3.icon.brand },
),
title = stringReference("Bank transfer"),
description = stringReference("Receive fiat USD via ACH/FedWire"),
onClick = listener::onClickBankTransfer,
)
},
)
}.toPersistentList(),
dismiss = listener::onDismissAddFunds,
errorMessage = null,
)
}
}

View file

@ -0,0 +1,302 @@
package com.tangem.features.tangempay.ui
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withLink
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.ds2.button.Close
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.ds2.row.*
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_info_24
import com.tangem.core.ui.res.generated.icons.ic_sign_usd_32
import com.tangem.features.tangempay.entity.TangemPayVirtualAccountDepositUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import com.tangem.core.ui.R as CoreUiR
@Composable
internal fun TangemPayVirtualAccountDepositBottomSheet(state: TangemPayVirtualAccountDepositUM) {
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = state.onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
type = TangemBottomSheetType.Modal,
containerColor = TangemTheme.colors3.bg.secondary,
title = {
TangemTopBar(
type = TangemTopBarType.BottomSheet,
title = null,
endContent = { TangemButton.Close(onClick = state.onDismiss) },
)
},
content = { _ -> DepositContent(state) },
)
}
@Composable
private fun DepositContent(state: TangemPayVirtualAccountDepositUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens2.x4)
.padding(bottom = TangemTheme.dimens2.x4),
horizontalAlignment = Alignment.CenterHorizontally,
) {
IntroIcons(modifier = Modifier.padding(top = TangemTheme.dimens2.x4))
TitleText(
text = stringReference("Bank transfer might take 1-2 business days"),
modifier = Modifier.padding(top = TangemTheme.dimens2.x8),
)
SubtitleText(
text = stringReference("Received USD will be converted to USDC by 1:1 rate"),
modifier = Modifier.padding(top = TangemTheme.dimens2.x2),
)
FeesBlock(
fees = state.fees,
modifier = Modifier.padding(top = TangemTheme.dimens2.x6),
)
InfoNotification(
text = stringReference("Deposit via ACH or FedWire only. SWIFT transfers will be returned."),
modifier = Modifier.padding(top = TangemTheme.dimens2.x4),
)
TangemButton(
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens2.x4),
text = stringReference("Show details"),
variant = TangemButton.Variant.Primary,
size = TangemButton.Size.X12,
onClick = state.onShowDetailsClick,
)
if (state.shouldShowTermsAndConditions) {
TermsFooter(
onTermsClick = state.onTermsClick,
onPrivacyClick = state.onPrivacyClick,
modifier = Modifier.padding(top = TangemTheme.dimens2.x3),
)
}
}
}
@Composable
private fun FeesBlock(fees: ImmutableList<TangemPayVirtualAccountDepositUM.FeeRow>, modifier: Modifier = Modifier) {
Column(modifier = modifier.fillMaxWidth()) {
Text(
modifier = Modifier.padding(
start = TangemTheme.dimens2.x4,
bottom = TangemTheme.dimens2.x2,
),
text = stringReference("Fee for onramp").resolveReference(),
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
)
fees.forEachIndexed { index, fee ->
TangemRow(
contentLead = TangemRowContentLead.Equal,
verticalAlignment = TangemRowVerticalAlignment.Center,
divider = index != fees.lastIndex,
titleSlot = { TangemRowText(text = fee.title, role = TangemRowTextRole.Title) },
valueSlot = { TangemRowText(text = stringReference(fee.value), role = TangemRowTextRole.Value) },
)
}
}
}
@Composable
private fun InfoNotification(text: TextReference, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.clip(RoundedCornerShape(TangemTheme.dimens2.x4))
.background(TangemTheme.colors3.bg.status.infoSubtle)
.padding(TangemTheme.dimens2.x4),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x5),
imageVector = Icons.ic_info_24,
contentDescription = null,
tint = TangemTheme.colors3.icon.status.info,
)
Text(
text = text.resolveReference(),
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.primary,
)
}
}
@Composable
private fun TermsFooter(onTermsClick: () -> Unit, onPrivacyClick: () -> Unit, modifier: Modifier = Modifier) {
val linkStyle = SpanStyle(color = TangemTheme.colors3.text.primary)
val text = buildAnnotatedString {
append("By using service, you agree with provider ")
withLink(LinkAnnotation.Clickable(tag = "terms", linkInteractionListener = { onTermsClick() })) {
withStyle(linkStyle) { append("Terms of Use") }
}
append(" and ")
withLink(LinkAnnotation.Clickable(tag = "privacy", linkInteractionListener = { onPrivacyClick() })) {
withStyle(linkStyle) { append("Privacy Policy") }
}
}
Text(
modifier = modifier.fillMaxWidth(),
text = text,
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
textAlign = TextAlign.Center,
)
}
@Composable
private fun IntroIcons(modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(-TangemTheme.dimens2.x4),
) {
UsdIcon()
UsdcIcon()
}
}
@Composable
private fun UsdIcon(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.size(TangemTheme.dimens2.x20)
.clip(CircleShape)
.background(TangemTheme.colors3.bg.opaque.primary)
.border(width = 1.dp, color = TangemTheme.colors3.border.secondary, shape = CircleShape),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x8),
imageVector = Icons.ic_sign_usd_32,
contentDescription = null,
tint = TangemTheme.colors3.icon.primary,
)
}
}
@Composable
private fun UsdcIcon(modifier: Modifier = Modifier) {
Box(modifier = modifier.size(TangemTheme.dimens2.x20)) {
Image(
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
.border(width = 1.dp, color = TangemTheme.colors3.border.secondary, shape = CircleShape),
painter = painterResource(CoreUiR.drawable.img_usdc_16),
contentDescription = null,
)
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.size(TangemTheme.dimens2.x6)
.background(color = TangemTheme.colors3.bg.accent.violet, shape = CircleShape)
.border(
width = TangemTheme.dimens2.x0_5,
color = TangemTheme.colors3.bg.secondary,
shape = CircleShape,
),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x4),
painter = painterResource(CoreUiR.drawable.ic_polygon_22),
contentDescription = null,
tint = TangemTheme.colors3.icon.inverse,
)
}
}
}
@Composable
private fun TitleText(text: TextReference, modifier: Modifier = Modifier) {
Text(
modifier = modifier.fillMaxWidth(),
text = text.resolveReference(),
style = TangemTheme.typography3.heading.small,
color = TangemTheme.colors3.text.primary,
textAlign = TextAlign.Center,
)
}
@Composable
private fun SubtitleText(text: TextReference, modifier: Modifier = Modifier) {
Text(
modifier = modifier.fillMaxWidth(),
text = text.resolveReference(),
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
textAlign = TextAlign.Center,
)
}
private fun previewState(shouldShowTermsAndConditions: Boolean) = TangemPayVirtualAccountDepositUM(
fees = persistentListOf(
TangemPayVirtualAccountDepositUM.FeeRow(title = stringReference("ACH"), value = "$1"),
TangemPayVirtualAccountDepositUM.FeeRow(title = stringReference("FedWire"), value = "$11"),
),
shouldShowTermsAndConditions = shouldShowTermsAndConditions,
onShowDetailsClick = {},
onDismiss = {},
onTermsClick = {},
onPrivacyClick = {},
)
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DepositEligiblePreview() {
TangemThemePreviewRedesign {
DepositContent(
state = previewState(shouldShowTermsAndConditions = true),
modifier = Modifier.background(TangemTheme.colors3.bg.secondary),
)
}
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DepositAvailablePreview() {
TangemThemePreviewRedesign {
DepositContent(
state = previewState(shouldShowTermsAndConditions = false),
modifier = Modifier.background(TangemTheme.colors3.bg.secondary),
)
}
}