Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-18 13:12:28 +03:00
commit 1be96adc3b
9 changed files with 362 additions and 18 deletions

View file

@ -101,6 +101,7 @@ fun Preview_PrimaryEndIconButton_Disabled_InDarkTheme() {
/**
* Primary button with an icon at the beginning of the layout
*
* @param modifier button modifier
* @param text button text
* @param iconResId button icon res id
* @param enabled controls the enabled state of the button
@ -110,8 +111,14 @@ fun Preview_PrimaryEndIconButton_Disabled_InDarkTheme() {
* >Figma component</a>
*/
@Composable
fun PrimaryStartIconButton(text: String, @DrawableRes iconResId: Int, enabled: Boolean = true, onClicked: () -> Unit) {
PrimaryButtonRow(enabled = enabled, onClicked = onClicked) {
fun PrimaryStartIconButton(
modifier: Modifier = Modifier,
text: String,
@DrawableRes iconResId: Int,
enabled: Boolean = true,
onClicked: () -> Unit,
) {
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
Icon(
painter = painterResource(id = iconResId),
contentDescription = null,
@ -124,6 +131,7 @@ fun PrimaryStartIconButton(text: String, @DrawableRes iconResId: Int, enabled: B
/**
* Primary button with an icon at the end of the layout
*
* @param modifier button modifier
* @param text button text
* @param iconResId button icon res id
* @param enabled controls the enabled state of the button
@ -133,8 +141,14 @@ fun PrimaryStartIconButton(text: String, @DrawableRes iconResId: Int, enabled: B
* >Figma component</a>
*/
@Composable
fun PrimaryEndIconButton(text: String, @DrawableRes iconResId: Int, enabled: Boolean = true, onClicked: () -> Unit) {
PrimaryButtonRow(enabled = enabled, onClicked = onClicked) {
fun PrimaryEndIconButton(
modifier: Modifier = Modifier,
text: String,
@DrawableRes iconResId: Int,
enabled: Boolean = true,
onClicked: () -> Unit,
) {
PrimaryButtonRow(modifier = modifier, enabled = enabled, onClicked = onClicked) {
Text(text = text)
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing8)))
Icon(
@ -145,10 +159,15 @@ fun PrimaryEndIconButton(text: String, @DrawableRes iconResId: Int, enabled: Boo
}
@Composable
private fun PrimaryButtonRow(enabled: Boolean, onClicked: () -> Unit, content: @Composable (RowScope.() -> Unit)) {
private fun PrimaryButtonRow(
modifier: Modifier,
enabled: Boolean,
onClicked: () -> Unit,
content: @Composable (RowScope.() -> Unit),
) {
Button(
onClick = onClicked,
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.height(dimensionResource(R.dimen.size48)),
enabled = enabled,

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,21H8V7H19V21ZM19,5H8C7.47,5 6.961,5.211 6.586,5.586C6.211,5.961 6,6.47 6,7V21C6,21.53 6.211,22.039 6.586,22.414C6.961,22.789 7.47,23 8,23H19C19.53,23 20.039,22.789 20.414,22.414C20.789,22.039 21,21.53 21,21V7C21,6.47 20.789,5.961 20.414,5.586C20.039,5.211 19.53,5 19,5ZM16,1H4C3.47,1 2.961,1.211 2.586,1.586C2.211,1.961 2,2.47 2,3V17H4V3H16V1Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8C18.796,8 19.559,7.684 20.121,7.121C20.684,6.559 21,5.796 21,5C21,4.204 20.684,3.441 20.121,2.879C19.559,2.316 18.796,2 18,2C17.204,2 16.441,2.316 15.879,2.879C15.316,3.441 15,4.204 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9C5.204,9 4.441,9.316 3.879,9.879C3.316,10.441 3,11.204 3,12C3,12.796 3.316,13.559 3.879,14.121C4.441,14.684 5.204,15 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,18.226 20.612,17.483 20.065,16.935C19.517,16.388 18.774,16.08 18,16.08Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="common_share">Поделиться</string>
<string name="common_copy">Копировать</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="common_share">Share</string>
<string name="common_copy">Copy</string>
</resources>

View file

@ -0,0 +1,77 @@
package com.tangem.feature.referral.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TextColorType
import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.presentation.R
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_AgreementText_InLightTheme() {
TangemTheme(isDarkTheme = false) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tou_not_enroled_prefix, onClicked = {})
}
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_AgreementText_InDarkTheme() {
TangemTheme(isDarkTheme = true) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tou_not_enroled_prefix, onClicked = {})
}
}
}
@Composable
fun AgreementText(@StringRes firstPartResId: Int, onClicked: () -> Unit) {
val agreementText = annotatedAgreementString(firstPart = stringResource(firstPartResId))
ClickableText(
text = agreementText,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = dimensionResource(id = R.dimen.spacing54)),
style = MaterialTheme.typography.caption.copy(textAlign = TextAlign.Center),
maxLines = 2,
onClick = {
val clickableSpanStyle = requireNotNull(agreementText.spanStyles.getOrNull(1))
if (it in clickableSpanStyle.start..clickableSpanStyle.end) {
onClicked()
}
},
)
}
@Composable
private fun annotatedAgreementString(firstPart: String): AnnotatedString {
return buildAnnotatedString {
withStyle(SpanStyle(color = MaterialTheme.colors.textColor(type = TextColorType.TERTIARY))) {
append("$firstPart ")
}
withStyle(SpanStyle(color = MaterialTheme.colors.textColor(type = TextColorType.ACCENT))) {
append(stringResource(id = R.string.common_terms_and_conditions))
}
withStyle(SpanStyle(color = MaterialTheme.colors.textColor(type = TextColorType.TERTIARY))) {
append(" ${stringResource(id = R.string.referral_tou_suffix)}")
}
}
}

View file

@ -0,0 +1,139 @@
package com.tangem.feature.referral.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.PrimaryStartIconButton
import com.tangem.core.ui.components.SmallInfoCard
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TextColorType
import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.presentation.R
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_NonParticipateBottomBlock_InLightTheme() {
TangemTheme(isDarkTheme = false) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
NonParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
onAgreementClicked = {},
)
}
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_NonParticipateBottomBlock_InDarkTheme() {
TangemTheme(isDarkTheme = true) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
NonParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
onAgreementClicked = {},
)
}
}
}
@Composable
fun NonParticipateBottomBlock(
purchasedWalletCount: Int,
code: String,
onCopyClicked: () -> Unit,
onShareClicked: () -> Unit,
onAgreementClicked: () -> Unit,
) {
Column(
modifier = Modifier
.padding(
top = dimensionResource(id = R.dimen.spacing24),
bottom = dimensionResource(id = R.dimen.spacing16),
)
.padding(horizontal = dimensionResource(id = R.dimen.spacing16)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.spacing16)),
) {
SmallInfoCard(
startText = stringResource(id = R.string.referral_friends_bought_title),
endText = String.format(
stringResource(id = R.string.referral_wallets_purchased_count),
purchasedWalletCount,
),
)
PersonalCodeCard(code = code)
AdditionalButtons(onCopyClicked = onCopyClicked, onShareClicked = onShareClicked)
AgreementText(firstPartResId = R.string.referral_tou_enroled_prefix, onClicked = onAgreementClicked)
}
}
@Composable
private fun PersonalCodeCard(code: String) {
Column(
modifier = Modifier
.shadow(
elevation = dimensionResource(id = R.dimen.elevation2),
shape = RoundedCornerShape(dimensionResource(id = R.dimen.radius12)),
)
.background(
color = MaterialTheme.colors.secondary,
shape = RoundedCornerShape(dimensionResource(id = R.dimen.radius12)),
)
.fillMaxWidth()
.padding(vertical = dimensionResource(id = R.dimen.spacing12)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.spacing4)),
) {
Text(
text = stringResource(id = R.string.referral_promo_code_title),
color = MaterialTheme.colors.textColor(type = TextColorType.TERTIARY),
maxLines = 1,
style = MaterialTheme.typography.subtitle2,
)
Text(
text = code,
color = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY1),
maxLines = 1,
style = MaterialTheme.typography.h2,
)
}
}
@Composable
private fun AdditionalButtons(onCopyClicked: () -> Unit, onShareClicked: () -> Unit) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.spacing16)),
) {
PrimaryStartIconButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_copy),
iconResId = R.drawable.ic_copy,
onClicked = onCopyClicked,
)
PrimaryStartIconButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_share),
iconResId = R.drawable.ic_share,
onClicked = onShareClicked,
)
}
}

View file

@ -0,0 +1,50 @@
package com.tangem.feature.referral.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.PrimaryEndIconButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.referral.presentation.R
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_ParticipateBottomBlock_InLightTheme() {
TangemTheme(isDarkTheme = false) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
ParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
}
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_ParticipateBottomBlock_InDarkTheme() {
TangemTheme(isDarkTheme = true) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
ParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
}
}
}
@Composable
fun ParticipateBottomBlock(onAgreementClicked: () -> Unit, onParticipateClicked: () -> Unit) {
Column {
AgreementText(
firstPartResId = R.string.referral_tou_not_enroled_prefix,
onClicked = onAgreementClicked,
)
PrimaryEndIconButton(
modifier = Modifier.padding(all = dimensionResource(id = R.dimen.spacing16)),
text = stringResource(id = R.string.referral_button_participate),
iconResId = R.drawable.ic_tangem,
onClicked = onParticipateClicked,
)
}
}

View file

@ -14,7 +14,9 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
@ -167,7 +169,8 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
Column(
modifier = Modifier
.background(color = MaterialTheme.colors.primary)
.fillMaxSize(),
.fillMaxSize()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Header(stateHolder = stateHolder)
@ -201,18 +204,20 @@ private fun ColumnScope.Header(stateHolder: ReferralStateHolder) {
@Composable
fun ReferralInfo(stateHolder: ReferralStateHolder) {
when (val state = stateHolder.referralInfoState) {
is ReferralInfoState.ParticipantContent,
is ReferralInfoState.ParticipantContent -> {
Conditions(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing44)
ParticipantBottomBlock(state = state)
}
is ReferralInfoState.NonParticipantContent -> {
Column(verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.spacing32))) {
ConditionForYou(state = state as ReferralStateHolder.ReferralInfoContentState)
ConditionForYourFriend(state = state)
}
Conditions(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing24)
NonParticipantBottomBlock(state = state)
}
is ReferralInfoState.Loading -> {
Column(verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.spacing32))) {
LoadingCondition(iconResId = R.drawable.ic_tether)
LoadingCondition(iconResId = R.drawable.ic_discount)
}
LoadingCondition(iconResId = R.drawable.ic_tether)
VerticalSpacer(spaceResId = R.dimen.spacing32)
LoadingCondition(iconResId = R.drawable.ic_discount)
}
}
}
@ -225,6 +230,13 @@ private fun AppBar(stateHolder: ReferralStateHolder) {
)
}
@Composable
private fun Conditions(state: ReferralStateHolder.ReferralInfoContentState) {
ConditionForYou(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing32)
ConditionForYourFriend(state = state)
}
@Composable
private fun ConditionForYou(state: ReferralStateHolder.ReferralInfoContentState) {
Condition(iconResId = R.drawable.ic_tether) {
@ -248,7 +260,7 @@ private fun ConditionForYourFriend(state: ReferralStateHolder.ReferralInfoConten
@Composable
private fun LoadingCondition(@DrawableRes iconResId: Int) {
Condition(iconResId = iconResId) {
ShimmerCondition()
ShimmerInfo()
}
}
@ -335,7 +347,7 @@ private fun ConditionInfo(title: String, subtitleContent: @Composable () -> Unit
}
@Composable
private fun ShimmerCondition() {
private fun ShimmerInfo() {
Column(
modifier = Modifier
.fillMaxWidth()
@ -360,3 +372,22 @@ private fun ShimmerCondition() {
}
}
@Composable
private fun ParticipantBottomBlock(state: ReferralInfoState.ParticipantContent) {
ParticipateBottomBlock(
onAgreementClicked = state.onAgreementClicked,
onParticipateClicked = state.onParticipateClicked,
)
}
@Composable
private fun NonParticipantBottomBlock(state: ReferralInfoState.NonParticipantContent) {
NonParticipateBottomBlock(
purchasedWalletCount = state.purchasedWalletCount,
code = state.code,
onCopyClicked = state.onCopyClicked,
onShareClicked = state.onShareClicked,
onAgreementClicked = state.onAgreementClicked,
)
}