Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-23 13:49:15 +03:00
parent 4a89075d5a
commit e17ff1b4c2
19 changed files with 391 additions and 177 deletions

View file

@ -175,6 +175,7 @@
<string name="onboarding_twin_exit_warning">Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас.</string> <string name="onboarding_twin_exit_warning">Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас.</string>
<string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуй позже.</string> <string name="wallet_balance_blockchain_unreachable_try_later">Блокчейн недоступен. Попробуй позже.</string>
<string name="warning_button_ok">Хорошо, понял!</string> <string name="warning_button_ok">Хорошо, понял!</string>
<string name="onboarding_success">Успешно!</string>
<string name="onboarding_getting_started">Начнем</string> <string name="onboarding_getting_started">Начнем</string>
<string name="onboarding_button_backup_now">Выполнить резервное копирование</string> <string name="onboarding_button_backup_now">Выполнить резервное копирование</string>
<string name="onboarding_button_skip_backup">Оставить на потом</string> <string name="onboarding_button_skip_backup">Оставить на потом</string>

View file

@ -153,6 +153,7 @@
<string name="warning_button_ok" translatable="false">Ok, Got it!</string> <string name="warning_button_ok" translatable="false">Ok, Got it!</string>
<string name="onboarding_success" translatable="false">Success!</string>
<string name="onboarding_getting_started" translatable="false">Getting started</string> <string name="onboarding_getting_started" translatable="false">Getting started</string>
<string name="onboarding_button_backup_now" translatable="false">Backup now</string> <string name="onboarding_button_backup_now" translatable="false">Backup now</string>
@ -222,7 +223,6 @@
<string name="wallet_connect_bnb_sign_message" translatable="false">Dapp %s, requesting to\n <string name="wallet_connect_bnb_sign_message" translatable="false">Dapp %s, requesting to\n
sign BNB transaction sign BNB transaction
\n \n

View file

@ -1,5 +1,6 @@
package com.tangem.core.ui.components.appbar package com.tangem.core.ui.components.appbar
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -18,22 +19,6 @@ import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInLightTheme() {
TangemTheme(isDark = false) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
}
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInDarkTheme() {
TangemTheme(isDark = true) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
}
/** /**
* App bar with back button and optional title * App bar with back button and optional title
* *
@ -47,8 +32,9 @@ fun PreviewAppBarWithBackButtonInDarkTheme() {
fun AppBarWithBackButton(text: String? = null, onBackClick: () -> Unit) { fun AppBarWithBackButton(text: String? = null, onBackClick: () -> Unit) {
Row( Row(
modifier = Modifier modifier = Modifier
.padding(all = dimensionResource(R.dimen.spacing16)) .background(MaterialTheme.colors.primary)
.fillMaxWidth(), .fillMaxWidth()
.padding(all = dimensionResource(R.dimen.spacing16)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing16)), horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing16)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
@ -69,4 +55,20 @@ fun AppBarWithBackButton(text: String? = null, onBackClick: () -> Unit) {
) )
} }
} }
}
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInLightTheme() {
TangemTheme(isDark = false) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
}
@Preview(widthDp = 360, heightDp = 56, showBackground = true)
@Composable
fun PreviewAppBarWithBackButtonInDarkTheme() {
TangemTheme(isDark = true) {
AppBarWithBackButton(text = "Title", onBackClick = {})
}
} }

View file

@ -2,4 +2,5 @@
<resources> <resources>
<string name="common_share">Поделиться</string> <string name="common_share">Поделиться</string>
<string name="common_copy">Копировать</string> <string name="common_copy">Копировать</string>
<string name="common_terms_and_conditions">условия участия</string>
</resources> </resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="warning_button_ok">Хорошо, понял!</string>
</resources>

View file

@ -13,7 +13,9 @@
<!-- endregion Elevation --> <!-- endregion Elevation -->
<!-- region Radius --> <!-- region Radius -->
<dimen name="radius2">2dp</dimen>
<dimen name="radius6">6dp</dimen> <dimen name="radius6">6dp</dimen>
<dimen name="radius8">8dp</dimen>
<dimen name="radius12">12dp</dimen> <dimen name="radius12">12dp</dimen>
<dimen name="radius16">16dp</dimen> <dimen name="radius16">16dp</dimen>
<!-- endregion Radius --> <!-- endregion Radius -->
@ -43,12 +45,14 @@
<dimen name="spacing8">8dp</dimen> <dimen name="spacing8">8dp</dimen>
<dimen name="spacing10">10dp</dimen> <dimen name="spacing10">10dp</dimen>
<dimen name="spacing12">12dp</dimen> <dimen name="spacing12">12dp</dimen>
<dimen name="spacing14">14dp</dimen>
<dimen name="spacing16">16dp</dimen> <dimen name="spacing16">16dp</dimen>
<dimen name="spacing24">24dp</dimen> <dimen name="spacing24">24dp</dimen>
<dimen name="spacing32">32dp</dimen> <dimen name="spacing32">32dp</dimen>
<dimen name="spacing44">44dp</dimen> <dimen name="spacing44">44dp</dimen>
<dimen name="spacing50">50dp</dimen> <dimen name="spacing50">50dp</dimen>
<dimen name="spacing54">54dp</dimen> <dimen name="spacing54">54dp</dimen>
<dimen name="radius2">2dp</dimen> <dimen name="spacing56">56dp</dimen>
<dimen name="spacing92">92dp</dimen>
<!-- endregion Spacing --> <!-- endregion Spacing -->
</resources> </resources>

View file

@ -2,4 +2,5 @@
<resources> <resources>
<string name="common_share" translatable="false">Share</string> <string name="common_share" translatable="false">Share</string>
<string name="common_copy" translatable="false">Copy</string> <string name="common_copy" translatable="false">Copy</string>
<string name="common_terms_and_conditions">terms and conditions</string>
</resources> </resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="warning_button_ok">Ok, Got it!</string>
</resources>

View file

@ -5,19 +5,22 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.tangem.feature.referral.router.ReferralRouter
import com.tangem.feature.referral.ui.ReferralScreen import com.tangem.feature.referral.ui.ReferralScreen
import com.tangem.feature.referral.viewmodels.ReferralViewModel import com.tangem.feature.referral.viewmodels.ReferralViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import java.lang.ref.WeakReference
@AndroidEntryPoint @AndroidEntryPoint
class ReferralFragment : Fragment() { class ReferralFragment : Fragment() {
private val viewModel by viewModels<ReferralViewModel>().apply { private val viewModel by viewModels<ReferralViewModel>()
// TODO("[REDACTED_JIRA]") this.value.setOnBackClicked()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) }
viewModel.setRouter(ReferralRouter(fragmentManager = WeakReference(parentFragmentManager)))
return ComposeView(inflater.context).apply { return ComposeView(inflater.context).apply {
setContent { setContent {
ReferralScreen(stateHolder = viewModel.uiState) ReferralScreen(stateHolder = viewModel.uiState)

View file

@ -1,15 +1,16 @@
package com.tangem.feature.referral.models package com.tangem.feature.referral.models
data class ReferralStateHolder( internal data class ReferralStateHolder(
val headerState: HeaderState, val headerState: HeaderState,
val referralInfoState: ReferralInfoState, val referralInfoState: ReferralInfoState,
val errorToast: ErrorToast, val errorSnackbar: ErrorSnackbar? = null,
) { ) {
data class HeaderState(val onBackClicked: () -> Unit) data class HeaderState(val onBackClicked: () -> Unit)
sealed interface ReferralInfoContentState { sealed interface ReferralInfoContentState {
val award: String val award: String
val networkName: String
val discount: String val discount: String
val url: String val url: String
} }
@ -17,15 +18,18 @@ data class ReferralStateHolder(
sealed interface ReferralInfoState { sealed interface ReferralInfoState {
data class ParticipantContent( data class ParticipantContent(
override val award: String, override val award: String,
override val networkName: String,
val address: String, val address: String,
override val discount: String, override val discount: String,
val purchasedWalletCount: Int, val purchasedWalletCount: Int,
val code: String, val code: String,
val shareLink: String,
override val url: String, override val url: String,
) : ReferralInfoState, ReferralInfoContentState ) : ReferralInfoState, ReferralInfoContentState
data class NonParticipantContent( data class NonParticipantContent(
override val award: String, override val award: String,
override val networkName: String,
override val discount: String, override val discount: String,
override val url: String, override val url: String,
val onParticipateClicked: () -> Unit, val onParticipateClicked: () -> Unit,
@ -34,8 +38,8 @@ data class ReferralStateHolder(
object Loading : ReferralInfoState object Loading : ReferralInfoState
} }
data class ErrorToast( data class ErrorSnackbar(
val visibility: Boolean, val throwable: Throwable,
val changeVisibility: () -> Unit val onOkClicked: () -> Unit,
) )
} }

View file

@ -0,0 +1,11 @@
package com.tangem.feature.referral.router
import androidx.fragment.app.FragmentManager
import java.lang.ref.WeakReference
internal class ReferralRouter(private val fragmentManager: WeakReference<FragmentManager>) {
fun back() {
fragmentManager.get()?.popBackStack()
}
}

View file

@ -29,7 +29,7 @@ import com.tangem.feature.referral.presentation.R
* @param url link to the html page * @param url link to the html page
*/ */
@Composable @Composable
fun AgreementBottomSheetContent(url: String) { internal fun AgreementBottomSheetContent(url: String) {
Column( Column(
modifier = Modifier modifier = Modifier
.background(color = MaterialTheme.colors.secondary) .background(color = MaterialTheme.colors.secondary)

View file

@ -22,28 +22,8 @@ import com.tangem.core.ui.res.TextColorType
import com.tangem.core.ui.res.textColor import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.presentation.R import com.tangem.feature.referral.presentation.R
@Preview(widthDp = 360, showBackground = true)
@Composable @Composable
fun Preview_AgreementText_InLightTheme() { internal fun AgreementText(@StringRes firstPartResId: Int, onClicked: () -> Unit) {
TangemTheme(isDark = false) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tos_not_enroled_prefix, onClicked = {})
}
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_AgreementText_InDarkTheme() {
TangemTheme(isDark = true) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tos_not_enroled_prefix, onClicked = {})
}
}
}
@Composable
fun AgreementText(@StringRes firstPartResId: Int, onClicked: () -> Unit) {
val agreementText = annotatedAgreementString(firstPart = stringResource(firstPartResId)) val agreementText = annotatedAgreementString(firstPart = stringResource(firstPartResId))
ClickableText( ClickableText(
text = agreementText, text = agreementText,
@ -74,4 +54,24 @@ private fun annotatedAgreementString(firstPart: String): AnnotatedString {
append(" ${stringResource(id = R.string.referral_tos_suffix)}") append(" ${stringResource(id = R.string.referral_tos_suffix)}")
} }
} }
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_AgreementText_InLightTheme() {
TangemTheme(isDark = false) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tos_not_enroled_prefix, onClicked = {})
}
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_AgreementText_InDarkTheme() {
TangemTheme(isDark = true) {
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) {
AgreementText(firstPartResId = R.string.referral_tos_not_enroled_prefix, onClicked = {})
}
}
} }

View file

@ -14,7 +14,7 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.referral.presentation.R import com.tangem.feature.referral.presentation.R
@Composable @Composable
fun NonParticipateBottomBlock(onAgreementClicked: () -> Unit, onParticipateClicked: () -> Unit) { internal fun NonParticipateBottomBlock(onAgreementClicked: () -> Unit, onParticipateClicked: () -> Unit) {
Column { Column {
AgreementText( AgreementText(
firstPartResId = R.string.referral_tos_not_enroled_prefix, firstPartResId = R.string.referral_tos_not_enroled_prefix,

View file

@ -1,5 +1,6 @@
package com.tangem.feature.referral.ui package com.tangem.feature.referral.ui
import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -14,8 +15,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
@ -29,10 +32,12 @@ import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.presentation.R import com.tangem.feature.referral.presentation.R
@Composable @Composable
fun ParticipateBottomBlock( internal fun ParticipateBottomBlock(
purchasedWalletCount: Int, purchasedWalletCount: Int,
code: String, code: String,
shareLink: String,
onAgreementClicked: () -> Unit, onAgreementClicked: () -> Unit,
showCopySnackbar: () -> Unit,
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@ -51,7 +56,7 @@ fun ParticipateBottomBlock(
), ),
) )
PersonalCodeCard(code = code) PersonalCodeCard(code = code)
AdditionalButtons(code = code) AdditionalButtons(shareLink = shareLink, showCopySnackbar = showCopySnackbar)
AgreementText(firstPartResId = R.string.referral_tos_enroled_prefix, onClicked = onAgreementClicked) AgreementText(firstPartResId = R.string.referral_tos_enroled_prefix, onClicked = onAgreementClicked)
} }
} }
@ -89,9 +94,9 @@ private fun PersonalCodeCard(code: String) {
} }
@Composable @Composable
private fun AdditionalButtons(code: String) { private fun AdditionalButtons(shareLink: String, showCopySnackbar: () -> Unit) {
val clipboardManager = LocalClipboardManager.current val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current val hapticFeedback = LocalHapticFeedback.current
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -101,25 +106,36 @@ private fun AdditionalButtons(code: String) {
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_copy), text = stringResource(id = R.string.common_copy),
iconResId = R.drawable.ic_copy, iconResId = R.drawable.ic_copy,
onClicked = { clipboardManager.setText(AnnotatedString(code)) }, onClicked = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
clipboardManager.setText(AnnotatedString(shareLink))
showCopySnackbar()
},
) )
val context = LocalContext.current
PrimaryStartIconButton( PrimaryStartIconButton(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_share), text = stringResource(id = R.string.common_share),
iconResId = R.drawable.ic_share, iconResId = R.drawable.ic_share,
onClicked = { onClicked = {
val sendIntent: Intent = Intent().apply { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
action = Intent.ACTION_SEND context.shareText(shareLink)
putExtra(Intent.EXTRA_TEXT, code)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(context, shareIntent, null)
}, },
) )
} }
} }
private fun Context.shareText(text: String) {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(this, shareIntent, null)
}
@Preview(widthDp = 360, showBackground = true) @Preview(widthDp = 360, showBackground = true)
@Composable @Composable
fun Preview_ParticipateBottomBlock_InLightTheme() { fun Preview_ParticipateBottomBlock_InLightTheme() {
@ -128,7 +144,9 @@ fun Preview_ParticipateBottomBlock_InLightTheme() {
ParticipateBottomBlock( ParticipateBottomBlock(
purchasedWalletCount = 3, purchasedWalletCount = 3,
code = "x4JdK", code = "x4JdK",
shareLink = "",
onAgreementClicked = {}, onAgreementClicked = {},
showCopySnackbar = {},
) )
} }
} }
@ -142,7 +160,9 @@ fun Preview_ParticipateBottomBlock_InDarkTheme() {
ParticipateBottomBlock( ParticipateBottomBlock(
purchasedWalletCount = 3, purchasedWalletCount = 3,
code = "x4JdK", code = "x4JdK",
shareLink = "",
onAgreementClicked = {}, onAgreementClicked = {},
showCopySnackbar = {},
) )
} }
} }

View file

@ -1,38 +1,50 @@
package com.tangem.feature.referral.ui package com.tangem.feature.referral.ui
import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomSheetScaffold import androidx.compose.material.BottomSheetScaffold
import androidx.compose.material.BottomSheetState import androidx.compose.material.BottomSheetState
import androidx.compose.material.BottomSheetValue import androidx.compose.material.BottomSheetValue
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Snackbar
import androidx.compose.material.SnackbarDuration
import androidx.compose.material.SnackbarHost
import androidx.compose.material.SnackbarHostState
import androidx.compose.material.SnackbarResult
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.rememberBottomSheetScaffoldState import androidx.compose.material.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.draw.shadow
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@ -41,8 +53,10 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.VerticalSpacer import com.tangem.core.ui.components.VerticalSpacer
import com.tangem.core.ui.components.appbar.AppBarWithBackButton import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.res.Black
import com.tangem.core.ui.res.ButtonColorType import com.tangem.core.ui.res.ButtonColorType
import com.tangem.core.ui.res.IconColorType import com.tangem.core.ui.res.IconColorType
import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemColorPalette
@ -52,6 +66,7 @@ import com.tangem.core.ui.res.buttonColor
import com.tangem.core.ui.res.iconColor import com.tangem.core.ui.res.iconColor
import com.tangem.core.ui.res.textColor import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.models.ReferralStateHolder import com.tangem.feature.referral.models.ReferralStateHolder
import com.tangem.feature.referral.models.ReferralStateHolder.ErrorSnackbar
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoContentState import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoContentState
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
import com.tangem.feature.referral.presentation.R import com.tangem.feature.referral.presentation.R
@ -65,7 +80,7 @@ import kotlinx.coroutines.launch
*/ */
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun ReferralScreen(stateHolder: ReferralStateHolder) { internal fun ReferralScreen(stateHolder: ReferralStateHolder) {
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState( val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed), bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed),
@ -75,7 +90,11 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
BottomSheetScaffold( BottomSheetScaffold(
sheetContent = { sheetContent = {
AgreementBottomSheetContent( AgreementBottomSheetContent(
url = (stateHolder.referralInfoState as? ReferralInfoContentState)?.url ?: "", url = when (val state = stateHolder.referralInfoState) {
is ReferralInfoState.NonParticipantContent -> state.url
is ReferralInfoState.ParticipantContent -> state.url
is ReferralInfoState.Loading -> ""
},
) )
}, },
scaffoldState = bottomSheetScaffoldState, scaffoldState = bottomSheetScaffoldState,
@ -100,66 +119,92 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
) )
}, },
) )
}
}
val context = LocalContext.current @OptIn(ExperimentalFoundationApi::class)
SideEffect { @Composable
if (stateHolder.errorToast.visibility) { private fun ReferralContent(stateHolder: ReferralStateHolder, onAgreementClicked: () -> Unit) {
//TODO("[REDACTED_JIRA]") val isCopyButtonPressed = remember { mutableStateOf(false) }
Toast.makeText(context, "", Toast.LENGTH_LONG).show()
stateHolder.errorToast.changeVisibility() Box {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
LazyColumn(
modifier = Modifier
.background(color = MaterialTheme.colors.primary)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
stickyHeader {
AppBarWithBackButton(
text = stringResource(R.string.details_referral_title),
onBackClick = stateHolder.headerState.onBackClicked,
)
}
item { Header() }
item {
ReferralInfo(
stateHolder = stateHolder,
onAgreementClicked = onAgreementClicked,
showCopySnackbar = { isCopyButtonPressed.value = true },
)
}
} }
} }
ErrorSnackbarHost(errorSnackbar = stateHolder.errorSnackbar)
CopySnackbarHost(isCopyButtonPressed = isCopyButtonPressed)
} }
} }
@Composable @Composable
fun ReferralContent(stateHolder: ReferralStateHolder, onAgreementClicked: () -> Unit) { private fun Header() {
Column( Column {
modifier = Modifier Image(
.background(color = MaterialTheme.colors.primary) painter = painterResource(R.drawable.ill_businessman_3d),
.fillMaxSize() contentDescription = null,
.verticalScroll(rememberScrollState()), modifier = Modifier
horizontalAlignment = Alignment.CenterHorizontally, .padding(horizontal = dimensionResource(id = R.dimen.spacing32))
) { .fillMaxWidth()
Header(stateHolder = stateHolder) .height(dimensionResource(R.dimen.size200)),
)
VerticalSpacer(spaceResId = R.dimen.spacing24)
Text(
text = stringResource(id = R.string.referral_title),
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing50)),
color = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY1),
textAlign = TextAlign.Center,
maxLines = 2,
style = MaterialTheme.typography.h2,
)
VerticalSpacer(spaceResId = R.dimen.spacing16) VerticalSpacer(spaceResId = R.dimen.spacing16)
ReferralInfo(stateHolder = stateHolder, onAgreementClicked = onAgreementClicked)
} }
} }
@Composable @Composable
private fun ColumnScope.Header(stateHolder: ReferralStateHolder) { private fun ReferralInfo(
AppBar(stateHolder = stateHolder) stateHolder: ReferralStateHolder,
Image( onAgreementClicked: () -> Unit,
painter = painterResource(R.drawable.ill_businessman_3d), showCopySnackbar: () -> Unit,
contentDescription = null, ) {
modifier = Modifier
.padding(horizontal = dimensionResource(id = R.dimen.spacing32))
.fillMaxWidth()
.height(dimensionResource(R.dimen.size200)),
)
VerticalSpacer(spaceResId = R.dimen.spacing24)
Text(
text = stringResource(id = R.string.referral_title),
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing50)),
color = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY1),
textAlign = TextAlign.Center,
maxLines = 2,
style = MaterialTheme.typography.h2,
)
}
@Composable
fun ReferralInfo(stateHolder: ReferralStateHolder, onAgreementClicked: () -> Unit) {
when (val state = stateHolder.referralInfoState) { when (val state = stateHolder.referralInfoState) {
is ReferralInfoState.ParticipantContent -> { is ReferralInfoState.ParticipantContent -> {
Conditions(state = state) Conditions(state = state)
ParticipantBottomBlock(state = state, onAgreementClicked = onAgreementClicked) ParticipateBottomBlock(
purchasedWalletCount = state.purchasedWalletCount,
code = state.code,
shareLink = state.shareLink,
onAgreementClicked = onAgreementClicked,
showCopySnackbar = showCopySnackbar,
)
} }
is ReferralInfoState.NonParticipantContent -> { is ReferralInfoState.NonParticipantContent -> {
Conditions(state = state) Conditions(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing44) VerticalSpacer(spaceResId = R.dimen.spacing44)
NonParticipantBottomBlock(state = state, onAgreementClicked = onAgreementClicked) NonParticipateBottomBlock(
onAgreementClicked = onAgreementClicked,
onParticipateClicked = state.onParticipateClicked,
)
} }
is ReferralInfoState.Loading -> { is ReferralInfoState.Loading -> {
LoadingCondition(iconResId = R.drawable.ic_tether) LoadingCondition(iconResId = R.drawable.ic_tether)
@ -169,38 +214,34 @@ fun ReferralInfo(stateHolder: ReferralStateHolder, onAgreementClicked: () -> Uni
} }
} }
@Composable
private fun AppBar(stateHolder: ReferralStateHolder) {
AppBarWithBackButton(
text = stringResource(R.string.details_referral_title),
onBackClick = stateHolder.headerState.onBackClicked,
)
}
@Composable @Composable
private fun Conditions(state: ReferralInfoContentState) { private fun Conditions(state: ReferralInfoContentState) {
ConditionForYou(state = state) ConditionForYou(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing32) VerticalSpacer(spaceResId = R.dimen.spacing32)
ConditionForYourFriend(state = state) ConditionForYourFriend(discount = state.discount)
} }
@Composable @Composable
private fun ConditionForYou(state: ReferralInfoContentState) { private fun ConditionForYou(state: ReferralInfoContentState) {
Condition(iconResId = R.drawable.ic_tether) { Condition(iconResId = R.drawable.ic_tether) {
when (state) { when (state) {
is ReferralInfoState.ParticipantContent -> InfoForYou(award = state.award, address = state.address) is ReferralInfoState.ParticipantContent -> InfoForYou(
is ReferralInfoState.NonParticipantContent -> InfoForYou(award = state.award) award = state.award,
networkName = state.networkName,
address = state.address,
)
is ReferralInfoState.NonParticipantContent -> InfoForYou(
award = state.award,
networkName = state.networkName,
)
} }
} }
} }
@Composable @Composable
private fun ConditionForYourFriend(state: ReferralInfoContentState) { private fun ConditionForYourFriend(discount: String) {
Condition(iconResId = R.drawable.ic_discount) { Condition(iconResId = R.drawable.ic_discount) {
when (state) { InfoForYourFriend(discount = discount)
is ReferralInfoState.ParticipantContent -> InfoForYourFriend(discount = state.discount)
is ReferralInfoState.NonParticipantContent -> InfoForYourFriend(discount = state.discount)
}
} }
} }
@ -239,7 +280,7 @@ private fun Condition(@DrawableRes iconResId: Int, infoBlock: @Composable () ->
} }
@Composable @Composable
private fun InfoForYou(award: String, address: String? = null) { private fun InfoForYou(award: String, networkName: String, address: String? = null) {
ConditionInfo(title = stringResource(id = R.string.referral_point_currencies_title)) { ConditionInfo(title = stringResource(id = R.string.referral_point_currencies_title)) {
Text( Text(
text = buildAnnotatedString { text = buildAnnotatedString {
@ -250,7 +291,8 @@ private fun InfoForYou(award: String, address: String? = null) {
append( append(
String.format( String.format(
stringResource(id = R.string.referral_point_currencies_description_suffix), stringResource(id = R.string.referral_point_currencies_description_suffix),
if (!address.isNullOrBlank()) address else "", networkName,
if (!address.isNullOrBlank()) " $address" else "",
), ),
) )
}, },
@ -319,21 +361,105 @@ private fun ShimmerInfo() {
} }
} }
// TODO() Replace component with component from ds
@Composable @Composable
private fun ParticipantBottomBlock(state: ReferralInfoState.ParticipantContent, onAgreementClicked: () -> Unit) { private fun ErrorSnackbarHost(errorSnackbar: ErrorSnackbar?) {
ParticipateBottomBlock( if (errorSnackbar != null) {
purchasedWalletCount = state.purchasedWalletCount, val snackbarHostState by remember { mutableStateOf(SnackbarHostState()) }
code = state.code, val coroutineScope = rememberCoroutineScope()
onAgreementClicked = onAgreementClicked,
) SnackbarHost(
hostState = snackbarHostState,
modifier = Modifier.padding(top = dimensionResource(id = R.dimen.spacing56)),
snackbar = {
Snackbar(
snackbarData = it,
modifier = Modifier.fillMaxWidth(),
actionOnNewLine = true,
shape = RoundedCornerShape(size = dimensionResource(id = R.dimen.radius8)),
backgroundColor = Black,
contentColor = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY2),
actionColor = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY2),
elevation = dimensionResource(id = R.dimen.elevation3),
)
},
)
val message = if (errorSnackbar.throwable.cause != null) {
String.format(
format = stringResource(id = R.string.referral_error_failed_to_load_info_with_reason),
errorSnackbar.throwable.cause,
)
} else {
stringResource(id = R.string.referral_error_failed_to_load_info)
}
val actionLabel = stringResource(id = R.string.warning_button_ok)
SideEffect {
coroutineScope.launch {
val result = snackbarHostState.showSnackbar(
message = message,
actionLabel = actionLabel,
duration = SnackbarDuration.Indefinite,
)
if (result == SnackbarResult.ActionPerformed) {
errorSnackbar.onOkClicked()
}
}
}
}
} }
// TODO() Replace component with component from ds
@Composable @Composable
private fun NonParticipantBottomBlock(state: ReferralInfoState.NonParticipantContent, onAgreementClicked: () -> Unit) { private fun CopySnackbarHost(isCopyButtonPressed: MutableState<Boolean>) {
NonParticipateBottomBlock( if (isCopyButtonPressed.value) {
onAgreementClicked = onAgreementClicked, val snackbarHostState by remember { mutableStateOf(SnackbarHostState()) }
onParticipateClicked = state.onParticipateClicked, val coroutineScope = rememberCoroutineScope()
)
var snackbarSize by remember { mutableStateOf(0) }
val width = LocalConfiguration.current.screenWidthDp.dp
val snackbarWidth = with(LocalDensity.current) { snackbarSize.toDp() }
SnackbarHost(
hostState = snackbarHostState,
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.spacing56), start = (width - snackbarWidth).div(2))
.fillMaxWidth(),
snackbar = {
Box(
modifier = Modifier
.onSizeChanged { snackbarSize = it.width }
.background(Black, RoundedCornerShape(size = dimensionResource(id = R.dimen.radius8)))
.shadow(
dimensionResource(id = R.dimen.elevation3),
RoundedCornerShape(size = dimensionResource(id = R.dimen.radius8)),
)
.padding(
horizontal = dimensionResource(id = R.dimen.spacing16),
vertical = dimensionResource(id = R.dimen.spacing14),
),
) {
Text(
text = it.message,
color = MaterialTheme.colors.textColor(type = TextColorType.PRIMARY2),
style = MaterialTheme.typography.body2,
)
}
},
)
val message = stringResource(id = R.string.referral_promo_code_copied)
SideEffect {
coroutineScope.launch {
snackbarHostState.showSnackbar(
message = message,
duration = SnackbarDuration.Short,
)
isCopyButtonPressed.value = false
}
}
}
} }
@Preview(widthDp = 360, showBackground = true) @Preview(widthDp = 360, showBackground = true)
@ -345,13 +471,15 @@ fun Preview_ReferralScreen_Participant_InLightTheme() {
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.ParticipantContent( referralInfoState = ReferralInfoState.ParticipantContent(
award = "10 USDT", award = "10 USDT",
networkName = "Tron",
address = "ma80...zk8q2", address = "ma80...zk8q2",
discount = "10%", discount = "10%",
purchasedWalletCount = 3, purchasedWalletCount = 3,
code = "x4JdK", code = "x4JdK",
shareLink = "",
url = "", url = "",
), ),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }
@ -366,13 +494,15 @@ fun Preview_ReferralScreen_Participant_InDarkTheme() {
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.ParticipantContent( referralInfoState = ReferralInfoState.ParticipantContent(
award = "10 USDT", award = "10 USDT",
networkName = "Tron",
address = "ma80...zk8q2", address = "ma80...zk8q2",
discount = "10%", discount = "10%",
purchasedWalletCount = 3, purchasedWalletCount = 3,
code = "x4JdK", code = "x4JdK",
shareLink = "",
url = "", url = "",
), ),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }
@ -387,11 +517,12 @@ fun Preview_ReferralScreen_NonParticipant_InLightTheme() {
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.NonParticipantContent( referralInfoState = ReferralInfoState.NonParticipantContent(
award = "10 USDT", award = "10 USDT",
networkName = "Tron",
discount = "10%", discount = "10%",
url = "", url = "",
onParticipateClicked = {}, onParticipateClicked = {},
), ),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }
@ -406,11 +537,12 @@ fun Preview_ReferralScreen_NonParticipant_InDarkTheme() {
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.NonParticipantContent( referralInfoState = ReferralInfoState.NonParticipantContent(
award = "10 USDT", award = "10 USDT",
networkName = "Tron",
discount = "10%", discount = "10%",
url = "", url = "",
onParticipateClicked = {}, onParticipateClicked = {},
), ),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }
@ -424,7 +556,7 @@ fun Preview_ReferralScreen_Loading_InLightTheme() {
stateHolder = ReferralStateHolder( stateHolder = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.Loading, referralInfoState = ReferralInfoState.Loading,
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }
@ -438,7 +570,7 @@ fun Preview_ReferralScreen_Loading_InDarkTheme() {
stateHolder = ReferralStateHolder( stateHolder = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}), headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.Loading, referralInfoState = ReferralInfoState.Loading,
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}), errorSnackbar = null,
), ),
) )
} }

View file

@ -8,16 +8,20 @@ import androidx.lifecycle.viewModelScope
import com.tangem.feature.referral.domain.ReferralInteractor import com.tangem.feature.referral.domain.ReferralInteractor
import com.tangem.feature.referral.domain.models.DiscountType import com.tangem.feature.referral.domain.models.DiscountType
import com.tangem.feature.referral.domain.models.ReferralData import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.ReferralInfo
import com.tangem.feature.referral.models.ReferralStateHolder import com.tangem.feature.referral.models.ReferralStateHolder
import com.tangem.feature.referral.models.ReferralStateHolder.ErrorSnackbar
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
import com.tangem.feature.referral.router.ReferralRouter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching import com.tangem.utils.coroutines.runCatching
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
import kotlin.properties.Delegates
@HiltViewModel @HiltViewModel
class ReferralViewModel @Inject constructor( internal class ReferralViewModel @Inject constructor(
private val referralInteractor: ReferralInteractor, private val referralInteractor: ReferralInteractor,
private val dispatchers: CoroutineDispatcherProvider, private val dispatchers: CoroutineDispatcherProvider,
) : ViewModel() { ) : ViewModel() {
@ -25,57 +29,80 @@ class ReferralViewModel @Inject constructor(
var uiState: ReferralStateHolder by mutableStateOf(createInitiallyUiState()) var uiState: ReferralStateHolder by mutableStateOf(createInitiallyUiState())
private set private set
private var referralRouter: ReferralRouter by Delegates.notNull()
init { init {
loadReferralData() loadReferralData()
} }
fun setOnBackClicked(onBackClicked: () -> Unit) { fun setRouter(router: ReferralRouter) {
uiState = uiState.copy(headerState = ReferralStateHolder.HeaderState(onBackClicked = onBackClicked)) referralRouter = router
uiState = uiState.copy(headerState = ReferralStateHolder.HeaderState(onBackClicked = router::back))
} }
private fun createInitiallyUiState() = ReferralStateHolder( private fun createInitiallyUiState() = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = { }), headerState = ReferralStateHolder.HeaderState(onBackClicked = { }),
referralInfoState = ReferralInfoState.Loading, referralInfoState = ReferralInfoState.Loading,
errorToast = ReferralStateHolder.ErrorToast( errorSnackbar = null,
visibility = false,
changeVisibility = {
uiState = uiState.copy(
referralInfoState = uiState.referralInfoState,
errorToast = uiState.errorToast.copy(visibility = false),
)
},
),
) )
private fun loadReferralData() { private fun loadReferralData() {
val lastUiState = uiState
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading) uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) { viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.getReferralStatus() } runCatching(dispatchers.io) { referralInteractor.getReferralStatus() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) } .onSuccess(::showContent)
.onFailure { uiState = lastUiState.copy(errorToast = lastUiState.errorToast.copy(visibility = true)) } .onFailure(::showErrorSnackbar)
} }
} }
private fun participate() {
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.startReferral() }
.onSuccess(::showContent)
.onFailure(::showErrorSnackbar)
}
}
private fun showContent(referralData: ReferralData) {
uiState = uiState.copy(referralInfoState = referralData.convertToReferralInfoState())
}
private fun showErrorSnackbar(throwable: Throwable) {
uiState = uiState.copy(
errorSnackbar = ErrorSnackbar(throwable = throwable, onOkClicked = referralRouter::back),
)
}
private fun ReferralData.convertToReferralInfoState(): ReferralInfoState = when (this) { private fun ReferralData.convertToReferralInfoState(): ReferralInfoState = when (this) {
is ReferralData.ParticipantData -> ReferralInfoState.ParticipantContent( is ReferralData.ParticipantData -> ReferralInfoState.ParticipantContent(
award = getAwardValue(), award = getAwardValue(),
address = referral.address, networkName = getNetworkName(),
address = referral.getAddressValue(),
discount = getDiscountValue(), discount = getDiscountValue(),
purchasedWalletCount = referral.walletsPurchased, purchasedWalletCount = referral.walletsPurchased,
code = referral.promocode, code = referral.promocode,
shareLink = referral.shareLink,
url = tosLink, url = tosLink,
) )
is ReferralData.NonParticipantData -> ReferralInfoState.NonParticipantContent( is ReferralData.NonParticipantData -> ReferralInfoState.NonParticipantContent(
award = getAwardValue(), award = getAwardValue(),
networkName = getNetworkName(),
discount = getDiscountValue(), discount = getDiscountValue(),
url = tosLink, url = tosLink,
onParticipateClicked = ::onParticipateClicked, onParticipateClicked = ::participate,
) )
} }
private fun ReferralData.getAwardValue(): String = private fun ReferralData.getAwardValue(): String = "$award ${getToken().symbol}"
"$award ${requireNotNull(tokens.firstOrNull()?.symbol) { "Token list is empty" }}"
private fun ReferralData.getNetworkName(): String = getToken().networkId.replaceFirstChar(Char::uppercase)
private fun ReferralInfo.getAddressValue(): String {
check(address.length > 5) { "Invalid address" }
return address.substring(startIndex = 0, endIndex = 4) + "..." +
address.substring(startIndex = address.length - 5, endIndex = address.length)
}
private fun ReferralData.getDiscountValue(): String { private fun ReferralData.getDiscountValue(): String {
val discountSymbol = when (discountType) { val discountSymbol = when (discountType) {
@ -85,13 +112,5 @@ class ReferralViewModel @Inject constructor(
return "$discount $discountSymbol" return "$discount $discountSymbol"
} }
private fun onParticipateClicked() { private fun ReferralData.getToken() = requireNotNull(tokens.firstOrNull()) { "Token list is empty" }
val lastUiState = uiState
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.startReferral() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) }
.onFailure { uiState = lastUiState.copy(errorToast = lastUiState.errorToast.copy(visibility = true)) }
}
}
} }

View file

@ -4,7 +4,7 @@
<string name="referral_title">Приведи друга в Tangem</string> <string name="referral_title">Приведи друга в Tangem</string>
<string name="referral_point_currencies_title">Вы</string> <string name="referral_point_currencies_title">Вы</string>
<string name="referral_point_currencies_description_prefix">Получите</string> <string name="referral_point_currencies_description_prefix">Получите</string>
<string name="referral_point_currencies_description_suffix">за каждый кошелек, который купит ваш друг на ваш адрес в сети Tron %s</string> <string name="referral_point_currencies_description_suffix">на ваш адрес в сети %s%s за каждый кошелек, который купит ваш друг</string>
<string name="referral_point_discount_title">Ваш друг</string> <string name="referral_point_discount_title">Ваш друг</string>
<string name="referral_point_discount_description_prefix">Получит</string> <string name="referral_point_discount_description_prefix">Получит</string>
<string name="referral_point_discount_description_value">%s скидку</string> <string name="referral_point_discount_description_value">%s скидку</string>
@ -17,4 +17,8 @@
<string name="referral_promo_code_title">Ваш персональный код</string> <string name="referral_promo_code_title">Ваш персональный код</string>
<string name="referral_wallets_purchased_count">%d кошельков</string> <string name="referral_wallets_purchased_count">%d кошельков</string>
<string name="referral_friends_bought_title">Ваши друзья купили</string> <string name="referral_friends_bought_title">Ваши друзья купили</string>
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Причина: %s. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_participate">Не удалось обработать вашу заявку на участие. Причина: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку.</string>
<string name="referral_promo_code_copied">Персональный код скопирован!</string>
</resources> </resources>

View file

@ -4,7 +4,7 @@
<string name="referral_title">Refer your friends to Tangem</string> <string name="referral_title">Refer your friends to Tangem</string>
<string name="referral_point_currencies_title">You</string> <string name="referral_point_currencies_title">You</string>
<string name="referral_point_currencies_description_prefix">Will get</string> <string name="referral_point_currencies_description_prefix">Will get</string>
<string name="referral_point_currencies_description_suffix">for each wallet bought by your friend on your Tron network address %s</string> <string name="referral_point_currencies_description_suffix">for each wallet bought by your friend on your %s network address%s</string>
<string name="referral_point_discount_title">Your friend</string> <string name="referral_point_discount_title">Your friend</string>
<string name="referral_point_discount_description_prefix">Will get a</string> <string name="referral_point_discount_description_prefix">Will get a</string>
<string name="referral_point_discount_description_value">%s discount</string> <string name="referral_point_discount_description_value">%s discount</string>
@ -17,4 +17,8 @@
<string name="referral_promo_code_title">Your personal code</string> <string name="referral_promo_code_title">Your personal code</string>
<string name="referral_wallets_purchased_count">%d wallets</string> <string name="referral_wallets_purchased_count">%d wallets</string>
<string name="referral_friends_bought_title">Your friends bought</string> <string name="referral_friends_bought_title">Your friends bought</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>
<string name="referral_promo_code_copied">Personal code copied!</string>
</resources> </resources>