Updated on 2026-08-14
This commit is contained in:
parent
93b0b05e69
commit
164b6e3c71
19 changed files with 584 additions and 65 deletions
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.datasource.api.common
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.ToJson
|
||||
import org.joda.time.LocalDate
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
|
||||
class LocalDateAdapter : JsonAdapter<LocalDate>() {
|
||||
|
||||
private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd")
|
||||
|
||||
@FromJson
|
||||
override fun fromJson(reader: JsonReader): LocalDate? {
|
||||
val dateString = reader.nextString()
|
||||
return LocalDate.parse(dateString, formatter)
|
||||
}
|
||||
|
||||
@ToJson
|
||||
override fun toJson(writer: JsonWriter, value: LocalDate?) {
|
||||
if (value != null) {
|
||||
writer.value(formatter.print(value))
|
||||
} else {
|
||||
writer.nullValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import org.joda.time.LocalDate
|
||||
|
||||
/**
|
||||
* Main response class for referral API
|
||||
* contains all necessary info about users program status
|
||||
*/
|
||||
data class ReferralResponse(
|
||||
@Json(name = "conditions") val conditions: Conditions,
|
||||
@Json(name = "referral") val referral: Referral?,
|
||||
@Json(name = "expectedAwards") val expectedAwards: ExpectedAwards?,
|
||||
) {
|
||||
|
||||
data class Conditions(
|
||||
|
|
@ -45,4 +43,16 @@ data class ReferralResponse(
|
|||
@Json(name = "walletsPurchased") val walletsPurchased: Int,
|
||||
@Json(name = "termsAcceptedAt") val termsAcceptedAt: String?,
|
||||
)
|
||||
|
||||
data class ExpectedAwards(
|
||||
@Json(name = "numberOfWallets") val numberOfWallets: Int,
|
||||
@Json(name = "list") val list: List<AwardItem>,
|
||||
) {
|
||||
|
||||
data class AwardItem(
|
||||
@Json(name = "currency") val currency: String,
|
||||
@Json(name = "paymentDate") val paymentDate: LocalDate,
|
||||
@Json(name = "amount") val amount: Int,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.squareup.moshi.Moshi
|
|||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.common.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.LocalDateAdapter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -20,6 +21,7 @@ class MoshiModule {
|
|||
fun provideNetworkMoshi(): Moshi {
|
||||
return Moshi.Builder()
|
||||
.add(BigDecimalAdapter())
|
||||
.add(LocalDateAdapter())
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class NetworkModule {
|
|||
fun provideTangemTechApi(@NetworkMoshi moshi: Moshi): TangemTechApi {
|
||||
return Retrofit.Builder()
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.baseUrl(PROD_TANGEM_TECH_BASE_URL)
|
||||
.baseUrl(if (BuildConfig.DEBUG) DEV_TANGEM_TECH_BASE_URL else PROD_TANGEM_TECH_BASE_URL)
|
||||
.client(
|
||||
OkHttpClient.Builder()
|
||||
.addHeaders(
|
||||
|
|
|
|||
|
|
@ -325,8 +325,6 @@
|
|||
<item quantity="other">за %d кошельков</item>
|
||||
</plurals>
|
||||
<string name="referral_point_currencies_description">Получите ^^%1$s^^ на ваш адрес в сети %2$s %3$s ^^спустя 30 дней^^ за каждый кошелек, который купит ваш друг</string>
|
||||
<string name="referral_point_currencies_description_prefix">Получите</string>
|
||||
<string name="referral_point_currencies_description_suffix">на ваш адрес в сети %1$s%2$s за каждый кошелек, который купит ваш друг</string>
|
||||
<string name="referral_point_currencies_title">Вы</string>
|
||||
<string name="referral_point_discount_description_prefix">Получит</string>
|
||||
<string name="referral_point_discount_description_suffix">при покупке кошелька на сайте tangem.com</string>
|
||||
|
|
|
|||
|
|
@ -259,8 +259,6 @@
|
|||
<string name="referral_error_failed_to_load_info">無法加載有關推薦計劃的消息。請稍後再試</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">無法加載有關推薦計劃的消息。原因:%s。請稍後再試</string>
|
||||
<string name="referral_friends_bought_title">您的朋友買</string>
|
||||
<string name="referral_point_currencies_description_prefix">會得到</string>
|
||||
<string name="referral_point_currencies_description_suffix">對於你的朋友在你的 %1$s 網絡地址%2$s上購買的每個錢包</string>
|
||||
<string name="referral_point_currencies_title">你</string>
|
||||
<string name="referral_point_discount_description_prefix">得到</string>
|
||||
<string name="referral_point_discount_description_value">%s 折扣</string>
|
||||
|
|
|
|||
|
|
@ -320,8 +320,6 @@
|
|||
<item quantity="other">for %d wallets</item>
|
||||
</plurals>
|
||||
<string name="referral_point_currencies_description">Will get ^^%1$s^^ for each wallet bought by your friend on your %2$s network address %3$s ^^30 days after^^ that</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 %1$s network address%2$s</string>
|
||||
<string name="referral_point_currencies_title">You</string>
|
||||
<string name="referral_point_discount_description_prefix">Will get a</string>
|
||||
<string name="referral_point_discount_description_suffix">when buying a wallet on tangem.com</string>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
package com.tangem.feature.referral.converters
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import com.tangem.datasource.api.tangemTech.models.ReferralResponse
|
||||
import com.tangem.feature.referral.domain.models.DiscountType
|
||||
import com.tangem.feature.referral.domain.models.ReferralData
|
||||
import com.tangem.feature.referral.domain.models.ReferralInfo
|
||||
import com.tangem.feature.referral.domain.models.TokenData
|
||||
import com.tangem.feature.referral.domain.models.*
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.isToday
|
||||
import com.tangem.utils.extensions.isYesterday
|
||||
import com.tangem.utils.safeValueOf
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.DateTimeZone
|
||||
import org.joda.time.format.DateTimeFormatterBuilder
|
||||
import org.joda.time.format.ISODateTimeFormat
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class ReferralConverter @Inject constructor() : Converter<ReferralResponse, ReferralData> {
|
||||
|
|
@ -16,6 +20,7 @@ class ReferralConverter @Inject constructor() : Converter<ReferralResponse, Refe
|
|||
val conditions = value.conditions
|
||||
val referral = value.referral
|
||||
val tokenConverter = TokenConverter()
|
||||
val expectedAwardsConverter = ExpectedAwardsConverter()
|
||||
return if (referral != null) {
|
||||
ReferralData.ParticipantData(
|
||||
award = conditions.awards.firstOrNull()?.amount ?: 0,
|
||||
|
|
@ -32,6 +37,9 @@ class ReferralConverter @Inject constructor() : Converter<ReferralResponse, Refe
|
|||
ISODateTimeFormat.dateTimeParser().parseDateTime(referral.termsAcceptedAt)
|
||||
},
|
||||
),
|
||||
expectedAwards = value.expectedAwards?.let {
|
||||
expectedAwardsConverter.convert(it)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
ReferralData.NonParticipantData(
|
||||
|
|
@ -45,6 +53,47 @@ class ReferralConverter @Inject constructor() : Converter<ReferralResponse, Refe
|
|||
}
|
||||
}
|
||||
|
||||
private class ExpectedAwardsConverter : Converter<ReferralResponse.ExpectedAwards, ExpectedAwards> {
|
||||
|
||||
/** Example, 2 Aug, 2023 */
|
||||
private val dateFormatter by lazy {
|
||||
DateTimeFormatterBuilder()
|
||||
.appendDayOfMonth(1)
|
||||
.appendLiteral(' ')
|
||||
.appendMonthOfYearShortText()
|
||||
.appendLiteral(", ")
|
||||
.appendYear(4, 4)
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
|
||||
override fun convert(value: ReferralResponse.ExpectedAwards): ExpectedAwards {
|
||||
return ExpectedAwards(
|
||||
numberOfWallets = value.numberOfWallets,
|
||||
expectedAwards = value.list.map {
|
||||
ExpectedAward(
|
||||
paymentDate = it.paymentDate.toDateTimeAtStartOfDay().millis.toDateFormat(),
|
||||
amount = "${it.amount} ${it.currency}",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun Long.toDateFormat(): String {
|
||||
val localDate = DateTime(this, DateTimeZone.getDefault())
|
||||
return if (localDate.isToday() || localDate.isYesterday()) {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
this,
|
||||
DateTime.now().millis,
|
||||
DateUtils.DAY_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE,
|
||||
).toString()
|
||||
} else {
|
||||
dateFormatter.print(localDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TokenConverter : Converter<ReferralResponse.Conditions.Award.Token, TokenData> {
|
||||
|
||||
override fun convert(value: ReferralResponse.Conditions.Award.Token): TokenData {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ internal class ReferralRepositoryImpl @Inject constructor(
|
|||
override val isDemoMode: Boolean
|
||||
get() = demoModeDatasource.isDemoModeActive
|
||||
|
||||
override suspend fun getReferralStatus(walletId: String): ReferralData {
|
||||
override suspend fun getReferralData(walletId: String): ReferralData {
|
||||
return withContext(coroutineDispatcher.io) {
|
||||
referralConverter.convert(
|
||||
referralApi.getReferralStatus(
|
||||
|
|
|
|||
|
|
@ -20,9 +20,11 @@ internal class ReferralInteractorImpl(
|
|||
get() = repository.isDemoMode
|
||||
|
||||
override suspend fun getReferralStatus(): ReferralData {
|
||||
val refStatus = repository.getReferralStatus(userWalletManager.getWalletId())
|
||||
saveRefTokens(refStatus.tokens)
|
||||
return refStatus
|
||||
val referralData = repository.getReferralData(userWalletManager.getWalletId())
|
||||
|
||||
saveReferralTokens(referralData.tokens)
|
||||
|
||||
return referralData
|
||||
}
|
||||
|
||||
override suspend fun startReferral(): ReferralData {
|
||||
|
|
@ -37,7 +39,7 @@ internal class ReferralInteractorImpl(
|
|||
address = publicAddress,
|
||||
)
|
||||
} else {
|
||||
error("tokens for ref is empty")
|
||||
error("Tokens for ref is empty")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ internal class ReferralInteractorImpl(
|
|||
return derivationPath
|
||||
}
|
||||
|
||||
private fun saveRefTokens(tokens: List<TokenData>) {
|
||||
private fun saveReferralTokens(tokens: List<TokenData>) {
|
||||
tokensForReferral.clear()
|
||||
tokensForReferral.addAll(tokens)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ interface ReferralRepository {
|
|||
val isDemoMode: Boolean
|
||||
|
||||
/** Returns data object of [ReferralData] depends on user program status */
|
||||
suspend fun getReferralStatus(walletId: String): ReferralData
|
||||
suspend fun getReferralData(walletId: String): ReferralData
|
||||
|
||||
/** Starts user referral program */
|
||||
suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ sealed interface ReferralData {
|
|||
override val tosLink: String,
|
||||
override val tokens: List<TokenData>,
|
||||
val referral: ReferralInfo,
|
||||
val expectedAwards: ExpectedAwards?,
|
||||
) : ReferralData
|
||||
|
||||
/** Data class that used if user is not participant of program */
|
||||
|
|
@ -47,6 +48,16 @@ data class ReferralInfo(
|
|||
val termsAcceptedAt: DateTime?,
|
||||
)
|
||||
|
||||
data class ExpectedAwards(
|
||||
val numberOfWallets: Int,
|
||||
val expectedAwards: List<ExpectedAward>,
|
||||
)
|
||||
|
||||
data class ExpectedAward(
|
||||
val paymentDate: String,
|
||||
val amount: String,
|
||||
)
|
||||
|
||||
enum class DiscountType {
|
||||
PERCENTAGE, VALUE
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.referral.models
|
||||
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAwards
|
||||
|
||||
internal data class ReferralStateHolder(
|
||||
val headerState: HeaderState,
|
||||
val referralInfoState: ReferralInfoState,
|
||||
|
|
@ -26,6 +28,7 @@ internal data class ReferralStateHolder(
|
|||
val code: String,
|
||||
val shareLink: String,
|
||||
override val url: String,
|
||||
val expectedAwards: ExpectedAwards?,
|
||||
) : ReferralInfoState, ReferralInfoContentState
|
||||
|
||||
data class NonParticipantContent(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -43,13 +44,16 @@ internal fun AgreementBottomSheetContent(url: String) {
|
|||
@Composable
|
||||
private fun AgreementHtmlView(url: String) {
|
||||
val state = rememberWebViewState(url)
|
||||
val isInPreviewMode = LocalInspectionMode.current
|
||||
WebView(
|
||||
state = state,
|
||||
modifier = Modifier.background(TangemTheme.colors.background.secondary),
|
||||
onCreated = {
|
||||
it.settings.apply {
|
||||
javaScriptEnabled = false
|
||||
allowFileAccess = false
|
||||
if (!isInPreviewMode) {
|
||||
it.settings.apply {
|
||||
javaScriptEnabled = false
|
||||
allowFileAccess = false
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.feature.referral.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun AwardText(
|
||||
startText: String,
|
||||
startTextColor: Color,
|
||||
startTextStyle: TextStyle,
|
||||
endText: String,
|
||||
endTextColor: Color,
|
||||
endTextStyle: TextStyle,
|
||||
cornersToRound: CornersToRound,
|
||||
) {
|
||||
Surface(
|
||||
shape = cornersToRound.getShape(),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = startText,
|
||||
color = startTextColor,
|
||||
maxLines = 1,
|
||||
style = startTextStyle,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = endText,
|
||||
color = endTextColor,
|
||||
maxLines = 1,
|
||||
style = endTextStyle,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_AwardItem_Light() {
|
||||
TangemTheme {
|
||||
AwardText(
|
||||
startText = "startText",
|
||||
startTextColor = TangemTheme.colors.text.tertiary,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = "endText",
|
||||
endTextColor = TangemTheme.colors.text.primary1,
|
||||
endTextStyle = TangemTheme.typography.subtitle2,
|
||||
cornersToRound = CornersToRound.TOP_2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.feature.referral.ui
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
internal enum class CornersToRound {
|
||||
|
||||
ALL_4,
|
||||
TOP_2,
|
||||
BOTTOM_2,
|
||||
ZERO,
|
||||
;
|
||||
|
||||
@Suppress("TopLevelComposableFunctions")
|
||||
@Composable
|
||||
fun getShape(): RoundedCornerShape {
|
||||
val radius = TangemTheme.dimens.radius12
|
||||
return when (this) {
|
||||
ALL_4 -> RoundedCornerShape(radius)
|
||||
TOP_2 -> RoundedCornerShape(topStart = radius, topEnd = radius)
|
||||
BOTTOM_2 -> RoundedCornerShape(bottomStart = radius, bottomEnd = radius)
|
||||
ZERO -> RoundedCornerShape(0.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,19 @@ package com.tangem.feature.referral.ui
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.animation.*
|
||||
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.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -19,14 +23,16 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
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.components.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAward
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAwards
|
||||
import com.tangem.feature.referral.presentation.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -36,6 +42,7 @@ internal fun ParticipateBottomBlock(
|
|||
purchasedWalletCount: Int,
|
||||
code: String,
|
||||
shareLink: String,
|
||||
expectedAwards: ExpectedAwards?,
|
||||
onAgreementClick: () -> Unit,
|
||||
onShowCopySnackbar: () -> Unit,
|
||||
onCopyClick: () -> Unit,
|
||||
|
|
@ -50,14 +57,6 @@ internal fun ParticipateBottomBlock(
|
|||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
SmallInfoCard(
|
||||
startText = stringResource(id = R.string.referral_friends_bought_title),
|
||||
endText = pluralStringResource(
|
||||
id = R.plurals.referral_wallets_purchased_count,
|
||||
count = purchasedWalletCount,
|
||||
purchasedWalletCount,
|
||||
),
|
||||
)
|
||||
PersonalCodeCard(code = code)
|
||||
AdditionalButtons(
|
||||
code = code,
|
||||
|
|
@ -66,10 +65,187 @@ internal fun ParticipateBottomBlock(
|
|||
onCopyClick = onCopyClick,
|
||||
onShareClick = onShareClick,
|
||||
)
|
||||
CounterAndAwards(purchasedWalletCount = purchasedWalletCount, expectedAwards = expectedAwards)
|
||||
AgreementText(firstPartResId = R.string.referral_tos_enroled_prefix, onClick = onAgreementClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CounterAndAwards(purchasedWalletCount: Int, expectedAwards: ExpectedAwards?) {
|
||||
Column {
|
||||
Counter(purchasedWalletCount, expectedAwards)
|
||||
|
||||
if (expectedAwards != null) {
|
||||
Awards(expectedAwards)
|
||||
} else if (purchasedWalletCount != 0) {
|
||||
EmptyUpcomingPayments()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Counter(purchasedWalletCount: Int, expectedAwards: ExpectedAwards?) {
|
||||
val isExpectedAwardsPresent = expectedAwards != null
|
||||
|
||||
AwardText(
|
||||
startText = stringResource(id = R.string.referral_friends_bought_title),
|
||||
startTextColor = TangemTheme.colors.text.tertiary,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = pluralStringResource(
|
||||
id = R.plurals.referral_wallets_purchased_count,
|
||||
count = purchasedWalletCount,
|
||||
purchasedWalletCount,
|
||||
),
|
||||
endTextColor = TangemTheme.colors.text.primary1,
|
||||
endTextStyle = TangemTheme.typography.body2,
|
||||
cornersToRound = if (isExpectedAwardsPresent || purchasedWalletCount != 0) {
|
||||
CornersToRound.TOP_2
|
||||
} else {
|
||||
CornersToRound.ALL_4
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun Awards(expectedAwards: ExpectedAwards) {
|
||||
val elementsCountToShowInLessMode = 3
|
||||
val isExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
)
|
||||
AwardText(
|
||||
startText = stringResource(id = R.string.referral_expected_awards),
|
||||
startTextColor = TangemTheme.colors.text.tertiary,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = pluralStringResource(
|
||||
id = R.plurals.referral_number_of_wallets,
|
||||
count = expectedAwards.numberOfWallets,
|
||||
expectedAwards.numberOfWallets,
|
||||
),
|
||||
endTextColor = TangemTheme.colors.text.tertiary,
|
||||
endTextStyle = TangemTheme.typography.body2,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
|
||||
val initialItems = expectedAwards.expectedAwards.take(elementsCountToShowInLessMode)
|
||||
val extraItems = expectedAwards.expectedAwards.drop(elementsCountToShowInLessMode)
|
||||
|
||||
initialItems.forEachIndexed { index, expectedAward ->
|
||||
AwardText(
|
||||
startText = expectedAward.paymentDate,
|
||||
startTextColor = TangemTheme.colors.text.primary1,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = expectedAward.amount,
|
||||
endTextColor = TangemTheme.colors.text.primary1,
|
||||
endTextStyle = TangemTheme.typography.subtitle2,
|
||||
cornersToRound = if (index == initialItems.size - 1 && extraItems.isEmpty()) {
|
||||
CornersToRound.BOTTOM_2
|
||||
} else {
|
||||
CornersToRound.ZERO
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isExpanded.value,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = shrinkVertically() + fadeOut(),
|
||||
) {
|
||||
ExtraItems(extraItems = extraItems)
|
||||
}
|
||||
|
||||
if (expectedAwards.expectedAwards.size > elementsCountToShowInLessMode) {
|
||||
LessMoreButton(isExpanded = isExpanded)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun EmptyUpcomingPayments() {
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
)
|
||||
AwardText(
|
||||
startText = stringResource(id = R.string.referral_expected_awards),
|
||||
startTextColor = TangemTheme.colors.text.tertiary,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = "",
|
||||
endTextColor = TangemTheme.colors.text.tertiary,
|
||||
endTextStyle = TangemTheme.typography.body2,
|
||||
cornersToRound = CornersToRound.BOTTOM_2,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LessMoreButton(isExpanded: MutableState<Boolean>) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(
|
||||
bottomStart = TangemTheme.dimens.radius12,
|
||||
bottomEnd = TangemTheme.dimens.radius12,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(TangemTheme.dimens.size48)
|
||||
.clickable { isExpanded.value = !isExpanded.value }
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing16,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = if (isExpanded.value) {
|
||||
stringResource(id = R.string.referral_less)
|
||||
} else {
|
||||
stringResource(id = R.string.referral_more)
|
||||
},
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
val chevronIcon = if (isExpanded.value) {
|
||||
painterResource(id = com.tangem.core.ui.R.drawable.ic_chevron_up_24)
|
||||
} else {
|
||||
painterResource(id = com.tangem.core.ui.R.drawable.ic_chevron_24)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = chevronIcon,
|
||||
tint = TangemTheme.colors.text.tertiary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExtraItems(extraItems: List<ExpectedAward>) {
|
||||
Column {
|
||||
extraItems.forEach { expectedAward ->
|
||||
AwardText(
|
||||
startText = expectedAward.paymentDate,
|
||||
startTextColor = TangemTheme.colors.text.primary1,
|
||||
startTextStyle = TangemTheme.typography.subtitle2,
|
||||
endText = expectedAward.amount,
|
||||
endTextColor = TangemTheme.colors.text.primary1,
|
||||
endTextStyle = TangemTheme.typography.subtitle2,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PersonalCodeCard(code: String) {
|
||||
Column(
|
||||
|
|
@ -157,11 +333,28 @@ private fun Context.shareText(text: String) {
|
|||
@Composable
|
||||
private fun Preview_ParticipateBottomBlock_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
ParticipateBottomBlock(
|
||||
purchasedWalletCount = 3,
|
||||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
expectedAwards = ExpectedAwards(
|
||||
numberOfWallets = 3,
|
||||
expectedAwards = listOf(
|
||||
ExpectedAward(
|
||||
amount = "10 USDT",
|
||||
paymentDate = "Today",
|
||||
),
|
||||
ExpectedAward(
|
||||
amount = "20 USDT",
|
||||
paymentDate = "6 Aug 2023",
|
||||
),
|
||||
ExpectedAward(
|
||||
amount = "30 USDT",
|
||||
paymentDate = "10 Aug 2023",
|
||||
),
|
||||
),
|
||||
),
|
||||
onAgreementClick = {},
|
||||
onShowCopySnackbar = {},
|
||||
onCopyClick = {},
|
||||
|
|
@ -173,13 +366,64 @@ private fun Preview_ParticipateBottomBlock_InLightTheme() {
|
|||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ParticipateBottomBlock_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
private fun Preview_ParticipateBottomBlock_Without_Awards_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
ParticipateBottomBlock(
|
||||
purchasedWalletCount = 3,
|
||||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
expectedAwards = null,
|
||||
onAgreementClick = {},
|
||||
onShowCopySnackbar = {},
|
||||
onCopyClick = {},
|
||||
onShareClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ParticipateBottomBlock_Without_Awards_And_Purchased_Wallets_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
ParticipateBottomBlock(
|
||||
purchasedWalletCount = 0,
|
||||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
expectedAwards = null,
|
||||
onAgreementClick = {},
|
||||
onShowCopySnackbar = {},
|
||||
onCopyClick = {},
|
||||
onShareClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun LessMoreButton_White() {
|
||||
TangemTheme(isDark = false) {
|
||||
LessMoreButton(
|
||||
isExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ParticipateBottomBlock_Without_Awards_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
Column(Modifier.background(TangemTheme.colors.background.secondary)) {
|
||||
ParticipateBottomBlock(
|
||||
purchasedWalletCount = 3,
|
||||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
expectedAwards = null,
|
||||
onAgreementClick = {},
|
||||
onShowCopySnackbar = {},
|
||||
onCopyClick = {},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.platform.LocalConfiguration
|
|||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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
|
||||
|
|
@ -31,6 +32,8 @@ import com.tangem.core.ui.components.SpacerH32
|
|||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAward
|
||||
import com.tangem.feature.referral.domain.models.ExpectedAwards
|
||||
import com.tangem.feature.referral.models.DemoModeException
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder
|
||||
import com.tangem.feature.referral.models.ReferralStateHolder.*
|
||||
|
|
@ -159,6 +162,7 @@ private fun ReferralInfo(
|
|||
purchasedWalletCount = state.purchasedWalletCount,
|
||||
code = state.code,
|
||||
shareLink = state.shareLink,
|
||||
expectedAwards = state.expectedAwards,
|
||||
onAgreementClick = onAgreementClick,
|
||||
onShowCopySnackbar = onShowCopySnackbar,
|
||||
onCopyClick = stateHolder.analytics.onCopyClicked,
|
||||
|
|
@ -253,25 +257,47 @@ private fun Condition(@DrawableRes iconResId: Int, infoBlock: @Composable () ->
|
|||
private fun InfoForYou(award: String, networkName: String, address: String? = null) {
|
||||
ConditionInfo(title = stringResource(id = R.string.referral_point_currencies_title)) {
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
append(stringResource(id = R.string.referral_point_currencies_description_prefix))
|
||||
withStyle(SpanStyle(color = TangemTheme.colors.text.primary1)) {
|
||||
append(" $award ")
|
||||
}
|
||||
append(
|
||||
String.format(
|
||||
stringResource(id = R.string.referral_point_currencies_description_suffix),
|
||||
networkName,
|
||||
if (!address.isNullOrBlank()) " $address" else "",
|
||||
),
|
||||
)
|
||||
},
|
||||
formatAwardConditionsString(
|
||||
quantity = award,
|
||||
network = networkName,
|
||||
address = if (!address.isNullOrBlank()) " $address" else "",
|
||||
),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatAwardConditionsString(quantity: String, network: String, address: String): AnnotatedString {
|
||||
val rawString = stringResource(R.string.referral_point_currencies_description, quantity, network, address)
|
||||
|
||||
val pattern = Regex("\\^\\^(.*?)\\^\\^")
|
||||
var startIndex = 0
|
||||
val annotatedString = buildAnnotatedString {
|
||||
pattern.findAll(rawString).forEach { matchResult ->
|
||||
val index = matchResult.range.first
|
||||
val matchedValue = matchResult.groups[1]?.value ?: ""
|
||||
|
||||
// appends unformatted part
|
||||
append(rawString.substring(startIndex, index))
|
||||
|
||||
// applies style on ^^-wrapped parts
|
||||
withStyle(SpanStyle(color = TangemTheme.colors.text.primary1)) {
|
||||
append(matchedValue)
|
||||
}
|
||||
|
||||
// goes to next part
|
||||
startIndex = matchResult.range.last + 1
|
||||
}
|
||||
|
||||
// appends remaining ending if exists
|
||||
append(rawString.substring(startIndex))
|
||||
}
|
||||
|
||||
return annotatedString
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoForYourFriend(discount: String) {
|
||||
ConditionInfo(title = stringResource(id = R.string.referral_point_discount_title)) {
|
||||
|
|
@ -464,6 +490,7 @@ private fun Preview_ReferralScreen_Participant_InLightTheme() {
|
|||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
url = "",
|
||||
expectedAwards = null,
|
||||
),
|
||||
errorSnackbar = null,
|
||||
analytics = Analytics(
|
||||
|
|
@ -492,6 +519,52 @@ private fun Preview_ReferralScreen_Participant_InDarkTheme() {
|
|||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
url = "",
|
||||
expectedAwards = null,
|
||||
),
|
||||
errorSnackbar = null,
|
||||
analytics = Analytics(
|
||||
onAgreementClicked = {},
|
||||
onCopyClicked = {},
|
||||
onShareClicked = {},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 360, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_ReferralScreen_Participant_With_Referrals_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
ReferralScreen(
|
||||
stateHolder = ReferralStateHolder(
|
||||
headerState = HeaderState(onBackClicked = {}),
|
||||
referralInfoState = ReferralInfoState.ParticipantContent(
|
||||
award = "10 USDT",
|
||||
networkName = "Tron",
|
||||
address = "ma80...zk8q2",
|
||||
discount = "10%",
|
||||
purchasedWalletCount = 3,
|
||||
code = "x4JdK",
|
||||
shareLink = "",
|
||||
url = "",
|
||||
expectedAwards = ExpectedAwards(
|
||||
numberOfWallets = 5,
|
||||
expectedAwards = listOf(
|
||||
ExpectedAward(
|
||||
amount = "10 USDT",
|
||||
paymentDate = "Today",
|
||||
),
|
||||
ExpectedAward(
|
||||
amount = "20 USDT",
|
||||
paymentDate = "6 Aug 2023",
|
||||
),
|
||||
ExpectedAward(
|
||||
amount = "30 USDT",
|
||||
paymentDate = "10 Aug 2023",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
errorSnackbar = null,
|
||||
analytics = Analytics(
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ internal class ReferralViewModel @Inject constructor(
|
|||
|
||||
private var referralRouter: ReferralRouter by Delegates.notNull()
|
||||
|
||||
private val lastReferralData = mutableStateOf<ReferralData?>(null)
|
||||
private var lastReferralData: ReferralData? = null
|
||||
|
||||
init {
|
||||
loadReferralData()
|
||||
|
|
@ -67,7 +67,7 @@ internal class ReferralViewModel @Inject constructor(
|
|||
viewModelScope.launch(dispatchers.main) {
|
||||
runCatching(dispatchers.io) {
|
||||
referralInteractor.getReferralStatus().apply {
|
||||
lastReferralData.value = this
|
||||
lastReferralData = this
|
||||
}
|
||||
}
|
||||
.onSuccess(::showContent)
|
||||
|
|
@ -84,14 +84,13 @@ internal class ReferralViewModel @Inject constructor(
|
|||
viewModelScope.launch(dispatchers.main) {
|
||||
runCatching(dispatchers.io) { referralInteractor.startReferral() }
|
||||
.onSuccess(::showContent)
|
||||
.onFailure {
|
||||
if (it is UserCancelledException) {
|
||||
val lastRefData = lastReferralData.value
|
||||
if (lastRefData != null) {
|
||||
showContent(lastRefData)
|
||||
.onFailure { throwable ->
|
||||
if (throwable is UserCancelledException) {
|
||||
lastReferralData?.let { referralData ->
|
||||
showContent(referralData)
|
||||
}
|
||||
} else {
|
||||
showErrorSnackbar(it)
|
||||
showErrorSnackbar(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -130,6 +129,7 @@ internal class ReferralViewModel @Inject constructor(
|
|||
code = referral.promocode,
|
||||
shareLink = referral.shareLink,
|
||||
url = tosLink,
|
||||
expectedAwards = expectedAwards,
|
||||
)
|
||||
is ReferralData.NonParticipantData -> ReferralInfoState.NonParticipantContent(
|
||||
award = getAwardValue(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue