Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-21 17:21:57 +03:00
parent 2803ac9eef
commit ee722c5f46
11 changed files with 327 additions and 198 deletions

View file

@ -6,6 +6,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.tangem.common.extensions.VoidCallback
import com.tangem.feature.referral.ReferralFragment
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.FragmentShareTransition
import com.tangem.tap.features.details.ui.appsettings.AppSettingsFragment
@ -108,5 +109,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.WalletDetails -> WalletDetailsFragment()
AppScreen.WalletConnectSessions -> WalletConnectFragment()
AppScreen.QrScan -> QrScanFragment()
AppScreen.ReferralProgram -> ReferralFragment()
}
}

View file

@ -22,4 +22,5 @@ enum class AppScreen(
AddTokens, AddCustomToken,
WalletConnectSessions,
QrScan,
ReferralProgram
}

View file

@ -0,0 +1,10 @@
package com.tangem.utils.coroutines
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
suspend fun <R> runCatching(dispatcher: CoroutineDispatcher, block: suspend () -> R): Result<R> {
return runCatching {
withContext(dispatcher) { block() }
}
}

View file

@ -34,10 +34,13 @@ android {
dependencies {
/** Core modules */
implementation(project(":core:utils"))
implementation(project(":core:ui"))
/** AndroidX */
implementation(AndroidX.appCompat)
implementation(AndroidX.fragmentKtx)
implementation(AndroidX.lifecycleViewModelKtx)
/** Compose */
implementation(Compose.foundation)

View file

@ -0,0 +1,27 @@
package com.tangem.feature.referral
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.tangem.feature.referral.ui.ReferralScreen
import com.tangem.feature.referral.viewmodels.ReferralViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class ReferralFragment : Fragment() {
private val viewModel by viewModels<ReferralViewModel>().apply {
// TODO("[REDACTED_JIRA]") this.value.setOnBackClicked()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return ComposeView(inflater.context).apply {
setContent {
ReferralScreen(stateHolder = viewModel.uiState)
}
}
}
}

View file

@ -3,36 +3,39 @@ package com.tangem.feature.referral.models
data class ReferralStateHolder(
val headerState: HeaderState,
val referralInfoState: ReferralInfoState,
val effects: Effects,
val agreementBottomSheetState: AgreementBottomSheetState
val errorToast: ErrorToast,
) {
data class HeaderState(val onBackClicked: () -> Unit)
sealed interface ReferralInfoContentState
sealed interface ReferralInfoContentState {
val award: String
val discount: String
val url: String
}
sealed interface ReferralInfoState {
data class ParticipantContent(
val award: String,
override val award: String,
val address: String,
val discount: String,
val onParticipateClicked: () -> Unit,
override val discount: String,
val purchasedWalletCount: Int,
val code: String,
override val url: String,
) : ReferralInfoState, ReferralInfoContentState
data class NonParticipantContent(
val award: String,
val discount: String,
val purchasedWalletCount: Int,
val code: String,
val onCopyClicked: () -> Unit,
val onShareClicked: () -> Unit,
override val award: String,
override val discount: String,
override val url: String,
val onParticipateClicked: () -> Unit,
) : ReferralInfoState, ReferralInfoContentState
object Loading : ReferralInfoState
}
data class Effects(val showErrorToast: Boolean)
data class AgreementBottomSheetState(val url: String)
data class ErrorToast(
val visibility: Boolean,
val changeVisibility: () -> Unit
)
}

View file

@ -1,11 +0,0 @@
package com.tangem.feature.referral.presentation
import androidx.lifecycle.ViewModel
import com.tangem.feature.referral.domain.ReferralInteractor
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class ReferralViewModel @Inject constructor(referralInteractor: ReferralInteractor) : ViewModel() {
}

View file

@ -1,40 +1,40 @@
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.components.PrimaryEndIconButton
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
@Composable
fun NonParticipateBottomBlock(onAgreementClicked: () -> Unit, onParticipateClicked: () -> Unit) {
Column {
AgreementText(
firstPartResId = R.string.referral_tos_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,
)
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_NonParticipateBottomBlock_InLightTheme() {
TangemTheme(isDark = false) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
NonParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
onAgreementClicked = {},
)
NonParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
}
}
}
@ -44,96 +44,7 @@ fun Preview_NonParticipateBottomBlock_InLightTheme() {
fun Preview_NonParticipateBottomBlock_InDarkTheme() {
TangemTheme(isDark = true) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
NonParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
onAgreementClicked = {},
)
NonParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
}
}
}
@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_tos_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

@ -1,24 +1,135 @@
package com.tangem.feature.referral.ui
import android.content.Intent
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.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.PrimaryEndIconButton
import androidx.core.content.ContextCompat.startActivity
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
@Composable
fun ParticipateBottomBlock(
purchasedWalletCount: Int,
code: String,
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(code = code)
AgreementText(firstPartResId = R.string.referral_tos_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(code: String) {
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
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 = { clipboardManager.setText(AnnotatedString(code)) },
)
PrimaryStartIconButton(
modifier = Modifier.weight(1f),
text = stringResource(id = R.string.common_share),
iconResId = R.drawable.ic_share,
onClicked = {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, code)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(context, shareIntent, null)
},
)
}
}
@Preview(widthDp = 360, showBackground = true)
@Composable
fun Preview_ParticipateBottomBlock_InLightTheme() {
TangemTheme(isDark = false) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
ParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
ParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onAgreementClicked = {},
)
}
}
}
@ -28,23 +139,11 @@ fun Preview_ParticipateBottomBlock_InLightTheme() {
fun Preview_ParticipateBottomBlock_InDarkTheme() {
TangemTheme(isDark = true) {
Column(Modifier.background(MaterialTheme.colors.primary)) {
ParticipateBottomBlock(onAgreementClicked = {}, onParticipateClicked = {})
ParticipateBottomBlock(
purchasedWalletCount = 3,
code = "x4JdK",
onAgreementClicked = {},
)
}
}
}
@Composable
fun ParticipateBottomBlock(onAgreementClicked: () -> Unit, onParticipateClicked: () -> Unit) {
Column {
AgreementText(
firstPartResId = R.string.referral_tos_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

@ -1,5 +1,6 @@
package com.tangem.feature.referral.ui
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@ -26,10 +27,12 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.rememberBottomSheetScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -49,6 +52,7 @@ import com.tangem.core.ui.res.buttonColor
import com.tangem.core.ui.res.iconColor
import com.tangem.core.ui.res.textColor
import com.tangem.feature.referral.models.ReferralStateHolder
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoContentState
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
import com.tangem.feature.referral.presentation.R
import com.valentinilk.shimmer.shimmer
@ -69,7 +73,11 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
TangemTheme {
BottomSheetScaffold(
sheetContent = { AgreementBottomSheetContent(stateHolder.agreementBottomSheetState.url) },
sheetContent = {
AgreementBottomSheetContent(
url = (stateHolder.referralInfoState as ReferralInfoContentState).url,
)
},
scaffoldState = bottomSheetScaffoldState,
sheetShape = RoundedCornerShape(
topStart = dimensionResource(id = R.dimen.radius16),
@ -92,6 +100,15 @@ fun ReferralScreen(stateHolder: ReferralStateHolder) {
)
},
)
val context = LocalContext.current
SideEffect {
if (stateHolder.errorToast.visibility) {
//TODO("[REDACTED_JIRA]")
Toast.makeText(context, "", Toast.LENGTH_LONG).show()
stateHolder.errorToast.changeVisibility()
}
}
}
}
@ -137,12 +154,11 @@ fun ReferralInfo(stateHolder: ReferralStateHolder, onAgreementClicked: () -> Uni
when (val state = stateHolder.referralInfoState) {
is ReferralInfoState.ParticipantContent -> {
Conditions(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing44)
ParticipantBottomBlock(state = state, onAgreementClicked = onAgreementClicked)
}
is ReferralInfoState.NonParticipantContent -> {
Conditions(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing24)
VerticalSpacer(spaceResId = R.dimen.spacing44)
NonParticipantBottomBlock(state = state, onAgreementClicked = onAgreementClicked)
}
is ReferralInfoState.Loading -> {
@ -162,14 +178,14 @@ private fun AppBar(stateHolder: ReferralStateHolder) {
}
@Composable
private fun Conditions(state: ReferralStateHolder.ReferralInfoContentState) {
private fun Conditions(state: ReferralInfoContentState) {
ConditionForYou(state = state)
VerticalSpacer(spaceResId = R.dimen.spacing32)
ConditionForYourFriend(state = state)
}
@Composable
private fun ConditionForYou(state: ReferralStateHolder.ReferralInfoContentState) {
private fun ConditionForYou(state: ReferralInfoContentState) {
Condition(iconResId = R.drawable.ic_tether) {
when (state) {
is ReferralInfoState.ParticipantContent -> InfoForYou(award = state.award, address = state.address)
@ -179,7 +195,7 @@ private fun ConditionForYou(state: ReferralStateHolder.ReferralInfoContentState)
}
@Composable
private fun ConditionForYourFriend(state: ReferralStateHolder.ReferralInfoContentState) {
private fun ConditionForYourFriend(state: ReferralInfoContentState) {
Condition(iconResId = R.drawable.ic_discount) {
when (state) {
is ReferralInfoState.ParticipantContent -> InfoForYourFriend(discount = state.discount)
@ -306,19 +322,17 @@ private fun ShimmerInfo() {
@Composable
private fun ParticipantBottomBlock(state: ReferralInfoState.ParticipantContent, onAgreementClicked: () -> Unit) {
ParticipateBottomBlock(
purchasedWalletCount = state.purchasedWalletCount,
code = state.code,
onAgreementClicked = onAgreementClicked,
onParticipateClicked = state.onParticipateClicked,
)
}
@Composable
private fun NonParticipantBottomBlock(state: ReferralInfoState.NonParticipantContent, onAgreementClicked: () -> Unit) {
NonParticipateBottomBlock(
purchasedWalletCount = state.purchasedWalletCount,
code = state.code,
onCopyClicked = state.onCopyClicked,
onShareClicked = state.onShareClicked,
onAgreementClicked = onAgreementClicked,
onParticipateClicked = state.onParticipateClicked,
)
}
@ -333,12 +347,11 @@ fun Preview_ReferralScreen_Participant_InLightTheme() {
award = "10 USDT",
address = "ma80...zk8q2",
discount = "10%",
onParticipateClicked = {},
),
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
purchasedWalletCount = 3,
code = "x4JdK",
url = "",
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}
@ -355,13 +368,11 @@ fun Preview_ReferralScreen_Participant_InDarkTheme() {
award = "10 USDT",
address = "ma80...zk8q2",
discount = "10%",
onParticipateClicked = {},
),
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
purchasedWalletCount = 3,
code = "x4JdK",
url = "",
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}
@ -377,16 +388,10 @@ fun Preview_ReferralScreen_NonParticipant_InLightTheme() {
referralInfoState = ReferralInfoState.NonParticipantContent(
award = "10 USDT",
discount = "10%",
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
),
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
url = "",
onParticipateClicked = {},
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}
@ -402,16 +407,10 @@ fun Preview_ReferralScreen_NonParticipant_InDarkTheme() {
referralInfoState = ReferralInfoState.NonParticipantContent(
award = "10 USDT",
discount = "10%",
purchasedWalletCount = 3,
code = "x4JdK",
onCopyClicked = {},
onShareClicked = {},
),
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
url = "",
onParticipateClicked = {},
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}
@ -425,10 +424,7 @@ fun Preview_ReferralScreen_Loading_InLightTheme() {
stateHolder = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.Loading,
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
url = "",
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}
@ -442,10 +438,7 @@ fun Preview_ReferralScreen_Loading_InDarkTheme() {
stateHolder = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = {}),
referralInfoState = ReferralInfoState.Loading,
effects = ReferralStateHolder.Effects(showErrorToast = false),
agreementBottomSheetState = ReferralStateHolder.AgreementBottomSheetState(
url = "",
),
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = {}),
),
)
}

View file

@ -0,0 +1,91 @@
package com.tangem.feature.referral.viewmodels
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.feature.referral.domain.ReferralInteractor
import com.tangem.feature.referral.domain.models.DiscountType
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.models.ReferralStateHolder
import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ReferralViewModel @Inject constructor(
private val referralInteractor: ReferralInteractor,
private val dispatchers: CoroutineDispatcherProvider,
) : ViewModel() {
var uiState by mutableStateOf(createInitiallyUiState())
private set
init {
loadReferralData()
}
fun setOnBackClicked(onBackClicked: () -> Unit) {
uiState = uiState.copy(headerState = ReferralStateHolder.HeaderState(onBackClicked = onBackClicked))
}
private fun createInitiallyUiState() = ReferralStateHolder(
headerState = ReferralStateHolder.HeaderState(onBackClicked = { }),
referralInfoState = ReferralInfoState.Loading,
errorToast = ReferralStateHolder.ErrorToast(visibility = false, changeVisibility = ::cancelErrorToast),
)
private fun loadReferralData() {
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.getReferralStatus() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) }
.onFailure { uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = true)) }
}
}
private fun ReferralData.convertToReferralInfoState(): ReferralInfoState = when (this) {
is ReferralData.ParticipantData -> ReferralInfoState.ParticipantContent(
award = getAwardValue(),
address = referral.address,
discount = getDiscountValue(),
purchasedWalletCount = referral.walletsPurchased,
code = referral.promocode,
url = tosLink,
)
is ReferralData.NonParticipantData -> ReferralInfoState.NonParticipantContent(
award = getAwardValue(),
discount = getDiscountValue(),
url = tosLink,
onParticipateClicked = ::onParticipateClicked,
)
}
private fun ReferralData.getAwardValue(): String =
"$award ${requireNotNull(tokens.firstOrNull()?.symbol) { "Token list is empty" }}"
private fun ReferralData.getDiscountValue(): String {
val discountSymbol = when (discountType) {
DiscountType.PERCENTAGE -> "%"
DiscountType.VALUE -> throw java.lang.IllegalStateException("Value doesn't support")
}
return "$discount $discountSymbol"
}
private fun onParticipateClicked() {
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) { referralInteractor.startReferral() }
.onSuccess { uiState = uiState.copy(referralInfoState = it.convertToReferralInfoState()) }
.onFailure { uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = true)) }
}
}
private fun cancelErrorToast() {
uiState = uiState.copy(errorToast = uiState.errorToast.copy(visibility = false))
}
}