Updated on 2026-08-14
This commit is contained in:
commit
949658d756
32 changed files with 258 additions and 72 deletions
|
|
@ -142,6 +142,17 @@
|
|||
android:scheme="tangem" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:scheme="tangem" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<!-- Disable android.startup completely. Used for Worker according doc -->
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.os.Build
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.net.toUri
|
||||
import coil.executeBlocking
|
||||
import coil.request.ImageRequest
|
||||
import com.google.firebase.messaging.FirebaseMessagingService
|
||||
|
|
@ -36,10 +37,11 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
|
|||
val notification = message.notification ?: return
|
||||
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID
|
||||
|
||||
// TODO refactoring: [REDACTED_JIRA]
|
||||
val intent = Intent(applicationContext, MainActivity::class.java)
|
||||
intent.putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
val intent = Intent(applicationContext, MainActivity::class.java).apply {
|
||||
data = message.data[DEEPLINK_KEY]?.toUri()
|
||||
putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
/* context = */ this,
|
||||
/* requestCode = */ PUSH_NOTIFICATION_REQUEST_CODE,
|
||||
|
|
@ -101,5 +103,7 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
|
|||
private companion object {
|
||||
const val TANGEM_CHANNEL_ID = "Tangem General" // General channel for notifications
|
||||
const val PUSH_NOTIFICATION_REQUEST_CODE = 123
|
||||
|
||||
const val DEEPLINK_KEY = "deeplink"
|
||||
}
|
||||
}
|
||||
|
|
@ -15,14 +15,14 @@ internal object PromoDomainModule {
|
|||
@Singleton
|
||||
fun provideShouldShowSwapPromoWalletUseCase(
|
||||
promoSettingsRepository: PromoRepository,
|
||||
): ShouldShowSwapPromoWalletUseCase {
|
||||
return ShouldShowSwapPromoWalletUseCase(promoSettingsRepository)
|
||||
): ShouldShowPromoWalletUseCase {
|
||||
return ShouldShowPromoWalletUseCase(promoSettingsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowSwapPromoTokenUseCase {
|
||||
return ShouldShowSwapPromoTokenUseCase(promoRepository)
|
||||
fun provideShouldShowSwapPromoTokenUseCase(promoRepository: PromoRepository): ShouldShowPromoTokenUseCase {
|
||||
return ShouldShowPromoTokenUseCase(promoRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -123,8 +123,12 @@ object PreferencesKeys {
|
|||
|
||||
val WALLETS_NFT_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "walletsNftEnabledStates") }
|
||||
|
||||
// region Promo
|
||||
fun getShouldShowStoriesKey(storyId: String) = booleanPreferencesKey("shouldShowStories_$storyId")
|
||||
|
||||
fun getShouldShowPromoKey(promoId: String) = booleanPreferencesKey("shouldShowPromo_$promoId")
|
||||
// endregion
|
||||
|
||||
// region Permission
|
||||
fun getShouldShowPermission(permission: String) = booleanPreferencesKey("shouldShowPushPermission_$permission")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
class ReferralDeepLink(
|
||||
val onReceive: () -> Unit,
|
||||
) : DeepLink(shouldHandleDelayed = true) {
|
||||
override val uri: String = "tangem://referral"
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
onReceive()
|
||||
}
|
||||
}
|
||||
|
|
@ -520,6 +520,9 @@
|
|||
<string name="nft_wallet_title">NFT collections</string>
|
||||
<string name="nft_wallet_unable_to_load">Unable to load the data</string>
|
||||
<string name="nft_receive_choose_network">Choose network</string>
|
||||
<string name="notification_referral_promo_button">Join Now</string>
|
||||
<string name="notification_referral_promo_text">Share your code - earn 5 USDT per sale. Your friend gets 10% OFF.</string>
|
||||
<string name="notification_referral_promo_title">Get REWARDS for every friend!</string>
|
||||
<string name="nft_details_last_sale_price">Last sale price</string>
|
||||
<string name="nft_details_rarity_label">Rarity label</string>
|
||||
<string name="nft_details_rarity_rank">Rarity rank</string>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import androidx.compose.ui.semantics.Role
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
|
||||
|
|
@ -55,6 +57,7 @@ fun Notification(
|
|||
subtitleColor: Color = TangemTheme.colors.text.tertiary,
|
||||
containerColor: Color? = null,
|
||||
iconTint: Color? = null,
|
||||
iconSize: Dp = 20.dp,
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
NotificationBaseContainer(
|
||||
|
|
@ -68,6 +71,7 @@ fun Notification(
|
|||
MainContent(
|
||||
iconResId = config.iconResId,
|
||||
iconTint = iconTint,
|
||||
iconSize = iconSize,
|
||||
title = config.title,
|
||||
subtitle = config.subtitle,
|
||||
subtitleColor = subtitleColor,
|
||||
|
|
@ -127,6 +131,7 @@ internal fun NotificationBaseContainer(
|
|||
private fun MainContent(
|
||||
iconResId: Int,
|
||||
iconTint: Color?,
|
||||
iconSize: Dp,
|
||||
title: TextReference?,
|
||||
subtitle: TextReference,
|
||||
subtitleColor: Color,
|
||||
|
|
@ -137,7 +142,7 @@ private fun MainContent(
|
|||
iconResId = iconResId,
|
||||
tint = iconTint,
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size20)
|
||||
.size(size = iconSize)
|
||||
.align(alignment = Alignment.CenterVertically),
|
||||
)
|
||||
|
||||
|
|
|
|||
BIN
core/ui/src/main/res/drawable/img_referral_promo.webp
Normal file
BIN
core/ui/src/main/res/drawable/img_referral_promo.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -22,6 +22,7 @@ dependencies {
|
|||
implementation(projects.domain.promo)
|
||||
implementation(projects.domain.promo.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.features.referral.domain)
|
||||
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,21 @@ import com.tangem.data.promo.converters.StoryContentResponseConverter
|
|||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys.getShouldShowStoriesKey
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||
import com.tangem.datasource.local.preferences.utils.store
|
||||
import com.tangem.datasource.local.promo.PromoStoriesStore
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.promo.models.StoryContent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.referral.domain.ReferralRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
|
@ -23,24 +28,41 @@ internal class DefaultPromoRepository(
|
|||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val promoStoriesStore: PromoStoriesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val referralRepository: ReferralRepository,
|
||||
) : PromoRepository {
|
||||
|
||||
private val storyContentConverter = StoryContentResponseConverter()
|
||||
|
||||
override fun isReadyToShowWalletSwapPromo(): Flow<Boolean> {
|
||||
return flowOf(false) // Use it on new promo action
|
||||
override fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean> {
|
||||
return appPreferencesStore.get(
|
||||
key = PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
|
||||
default = true,
|
||||
).map { shouldShow ->
|
||||
if (promoId == PromoId.Referral) {
|
||||
!referralRepository.isReferralParticipant(userWalletId) && shouldShow
|
||||
} else {
|
||||
shouldShow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReadyToShowTokenSwapPromo(): Flow<Boolean> {
|
||||
return flowOf(false) // Use it on new promo action
|
||||
override fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean> {
|
||||
return if (promoId == PromoId.Referral) {
|
||||
flowOf(false)
|
||||
} else {
|
||||
appPreferencesStore.get(
|
||||
PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
|
||||
default = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShowWalletSwapPromo() {
|
||||
// Use it on new promo action
|
||||
override suspend fun setNeverToShowWalletPromo(promoId: PromoId) {
|
||||
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
|
||||
}
|
||||
|
||||
override suspend fun setNeverToShowTokenSwapPromo() {
|
||||
// Use it on new promo action
|
||||
override suspend fun setNeverToShowTokenPromo(promoId: PromoId) {
|
||||
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
|
||||
}
|
||||
|
||||
override fun getStoryById(id: String): Flow<StoryContent?> = isReadyToShowStories(id).mapLatest {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi
|
|||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.promo.PromoStoriesStore
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
import com.tangem.feature.referral.domain.ReferralRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -23,12 +24,14 @@ internal object PromoDataModule {
|
|||
appPreferencesStore: AppPreferencesStore,
|
||||
promoStoriesStore: PromoStoriesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
referralRepository: ReferralRepository,
|
||||
): PromoRepository {
|
||||
return DefaultPromoRepository(
|
||||
tangemApi = tangemTechApi,
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
promoStoriesStore = promoStoriesStore,
|
||||
dispatchers = dispatchers,
|
||||
referralRepository = referralRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,4 +23,8 @@ data class PromoBanner(
|
|||
private companion object {
|
||||
const val ACTIVE_STATUS = "active"
|
||||
}
|
||||
}
|
||||
|
||||
enum class PromoId {
|
||||
Referral,
|
||||
}
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.promo.models.StoryContent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PromoRepository {
|
||||
|
||||
// region Promo
|
||||
fun isReadyToShowWalletSwapPromo(): Flow<Boolean>
|
||||
fun isReadyToShowWalletPromo(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean>
|
||||
|
||||
fun isReadyToShowTokenSwapPromo(): Flow<Boolean>
|
||||
fun isReadyToShowTokenPromo(promoId: PromoId): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWalletSwapPromo()
|
||||
suspend fun setNeverToShowWalletPromo(promoId: PromoId)
|
||||
|
||||
suspend fun setNeverToShowTokenSwapPromo()
|
||||
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
|
||||
// endregion
|
||||
|
||||
// region Stories
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowPromoTokenUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(promoId: PromoId): Flow<Boolean> = promoRepository.isReadyToShowTokenPromo(promoId)
|
||||
|
||||
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowTokenPromo(promoId)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class ShouldShowPromoWalletUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, promoId: PromoId): Flow<Boolean> {
|
||||
return flow {
|
||||
emit(false)
|
||||
emitAll(promoRepository.isReadyToShowWalletPromo(userWalletId, promoId))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun neverToShow(promoId: PromoId) = promoRepository.setNeverToShowWalletPromo(promoId)
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoTokenUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowTokenSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = promoRepository.setNeverToShowTokenSwapPromo()
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.tangem.domain.promo
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class ShouldShowSwapPromoWalletUseCase(private val promoRepository: PromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = promoRepository.isReadyToShowWalletSwapPromo()
|
||||
|
||||
suspend fun neverToShow() = promoRepository.setNeverToShowWalletSwapPromo()
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ dependencies {
|
|||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.txhistory.models)
|
||||
implementation(projects.domain.staking.models)
|
||||
implementation(projects.domain.promo.models)
|
||||
|
||||
/** Other dependencies */
|
||||
implementation(deps.kotlin.serialization)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.domain.tokens.model.warnings
|
||||
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -42,6 +43,7 @@ sealed class CryptoCurrencyWarning {
|
|||
data class Rent(val rent: BigDecimal, val exemptionAmount: BigDecimal) : CryptoCurrencyWarning()
|
||||
|
||||
data class SwapPromo(
|
||||
val promoId: PromoId,
|
||||
val startDateTime: DateTime,
|
||||
val endDateTime: DateTime,
|
||||
) : CryptoCurrencyWarning()
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ internal class ReferralRepositoryImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun isReferralParticipant(userWalletId: UserWalletId): Boolean {
|
||||
return getReferralData(userWalletId.stringValue) is ReferralData.ParticipantData
|
||||
}
|
||||
|
||||
override suspend fun startReferral(
|
||||
walletId: String,
|
||||
networkId: String,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ interface ReferralRepository {
|
|||
/** Returns data object of [ReferralData] depends on user program status */
|
||||
suspend fun getReferralData(walletId: String): ReferralData
|
||||
|
||||
/** Returns whether user is participating in referral program */
|
||||
suspend fun isReferralParticipant(userWalletId: UserWalletId): Boolean
|
||||
|
||||
/** Starts user referral program */
|
||||
suspend fun startReferral(walletId: String, networkId: String, tokenId: String, address: String): ReferralData
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
|||
|
||||
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
|
||||
|
|
@ -49,9 +50,9 @@ interface TokenDetailsClickIntents {
|
|||
|
||||
fun onGoToProviderClick(url: String)
|
||||
|
||||
fun onSwapPromoDismiss()
|
||||
fun onSwapPromoDismiss(promoId: PromoId)
|
||||
|
||||
fun onSwapPromoClick()
|
||||
fun onSwapPromoClick(promoId: PromoId)
|
||||
|
||||
fun onGenerateExtendedKey()
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ import com.tangem.domain.card.NetworkHasDerivationUseCase
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.onramp.model.OnrampSource
|
||||
import com.tangem.domain.promo.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.promo.ShouldShowPromoTokenUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
|
|
@ -101,7 +102,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
||||
private val shouldShowPromoTokenUseCase: ShouldShowPromoTokenUseCase,
|
||||
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
|
||||
private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
|
||||
private val getStakingEntryInfoUseCase: GetStakingEntryInfoUseCase,
|
||||
|
|
@ -811,9 +812,9 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
router.openUrl(url)
|
||||
}
|
||||
|
||||
override fun onSwapPromoDismiss() {
|
||||
override fun onSwapPromoDismiss(promoId: PromoId) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoTokenUseCase.neverToShow()
|
||||
shouldShowPromoTokenUseCase.neverToShow(promoId)
|
||||
analyticsEventsHandler.send(
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Token,
|
||||
|
|
@ -824,9 +825,9 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSwapPromoClick() {
|
||||
override fun onSwapPromoClick(promoId: PromoId) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoTokenUseCase.neverToShow()
|
||||
shouldShowPromoTokenUseCase.neverToShow(promoId)
|
||||
analyticsEventsHandler.send(
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Token,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.tokens.model.warnings.HederaWarnings
|
||||
import com.tangem.domain.tokens.model.warnings.KaspaWarnings
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.removeBy
|
||||
|
|
@ -98,8 +98,8 @@ internal class TokenDetailsNotificationConverter(
|
|||
is CryptoCurrencyWarning.SwapPromo -> SwapPromo(
|
||||
startDateTime = warning.startDateTime,
|
||||
endDateTime = warning.endDateTime,
|
||||
onSwapClick = clickIntents::onSwapPromoClick,
|
||||
onCloseClick = clickIntents::onSwapPromoDismiss,
|
||||
onSwapClick = { clickIntents.onSwapPromoClick(warning.promoId) },
|
||||
onCloseClick = { clickIntents.onSwapPromoDismiss(warning.promoId) },
|
||||
)
|
||||
is CryptoCurrencyWarning.BeaconChainShutdown -> NetworkShutdown(
|
||||
title = resourceReference(R.string.warning_beacon_chain_retirement_title),
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.nft.FetchNFTCollectionsUseCase
|
|||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
|
|
@ -356,9 +357,7 @@ internal class WalletModel @Inject constructor(
|
|||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
if (action.selectedWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
|
||||
fetchCurrencyStatusUseCase(userWalletId = action.selectedWallet.walletId)
|
||||
}
|
||||
fetchIfSingleWallet(action.selectedWallet)
|
||||
|
||||
if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) {
|
||||
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
|
||||
|
|
@ -385,6 +384,8 @@ internal class WalletModel @Inject constructor(
|
|||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
fetchIfSingleWallet(userWallet = action.selectedWallet)
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(
|
||||
prevWalletId = action.prevWalletId,
|
||||
|
|
@ -402,12 +403,7 @@ internal class WalletModel @Inject constructor(
|
|||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
if (action.selectedWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
|
||||
modelScope.launch {
|
||||
fetchCurrencyStatusUseCase(userWalletId = action.selectedWallet.walletId)
|
||||
.onLeft { Timber.e(it.toString()) }
|
||||
}
|
||||
}
|
||||
fetchIfSingleWallet(userWallet = action.selectedWallet)
|
||||
|
||||
stateHolder.update(
|
||||
AddWalletTransformer(
|
||||
|
|
@ -506,6 +502,15 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fetchIfSingleWallet(userWallet: UserWallet) {
|
||||
if (userWallet.scanResponse.cardTypesResolver.isSingleWallet()) {
|
||||
modelScope.launch {
|
||||
fetchCurrencyStatusUseCase(userWalletId = userWallet.walletId)
|
||||
.onLeft { Timber.e(it.toString()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
|
||||
override fun onAllowed() {
|
||||
analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.feature.wallet.child.wallet.model.intents
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.TangemBlogUrlBuilder
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
|
|
@ -13,7 +15,8 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
|
|||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.promo.ShouldShowSwapPromoWalletUseCase
|
||||
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
|
|
@ -67,7 +70,9 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onCloseRateAppWarningClick()
|
||||
|
||||
fun onCloseSwapPromoClick()
|
||||
fun onClosePromoClick(promoId: PromoId)
|
||||
|
||||
fun onPromoClick(promoId: PromoId)
|
||||
|
||||
fun onSupportClick()
|
||||
|
||||
|
|
@ -98,13 +103,14 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val appRouter: AppRouter,
|
||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
override fun onAddBackupCardClick() {
|
||||
|
|
@ -268,16 +274,31 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCloseSwapPromoClick() {
|
||||
override fun onClosePromoClick(promoId: PromoId) {
|
||||
analyticsEventHandler.send(
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
|
||||
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
|
||||
),
|
||||
if (promoId == PromoId.Referral) {
|
||||
MainScreen.ReferralPromoButtonDismiss
|
||||
} else {
|
||||
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
|
||||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = TokenSwapPromoAnalyticsEvent.ProgramName.Empty, // Use it on new promo action
|
||||
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
|
||||
)
|
||||
},
|
||||
)
|
||||
modelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoWalletUseCase.neverToShow()
|
||||
shouldShowPromoWalletUseCase.neverToShow(promoId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPromoClick(promoId: PromoId) {
|
||||
if (promoId == PromoId.Referral) {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
analyticsEventHandler.send(MainScreen.ReferralPromoButtonParticipate)
|
||||
modelScope.launch {
|
||||
shouldShowPromoWalletUseCase.neverToShow(promoId)
|
||||
appRouter.push(AppRoute.ReferralProgram(userWalletId = userWallet.walletId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.deeplink.global.ReferralDeepLink
|
||||
import com.tangem.core.deeplink.global.SellCurrencyDeepLink
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -54,6 +56,7 @@ internal class WalletDeepLinksHandler @Inject constructor(
|
|||
)
|
||||
|
||||
return buildList {
|
||||
addReferralDeepLink(userWallet)
|
||||
add(sellCurrencyDeepLink)
|
||||
if (onrampFeatureToggles.isFeatureEnabled || !userWallet.isMultiCurrency) {
|
||||
add(
|
||||
|
|
@ -95,4 +98,18 @@ internal class WalletDeepLinksHandler @Inject constructor(
|
|||
analyticsEventHandler.send(TokenScreenAnalyticsEvent.Bought(cryptoCurrency.symbol))
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<DeepLink>.addReferralDeepLink(userWallet: UserWallet) {
|
||||
add(
|
||||
ReferralDeepLink(
|
||||
onReceive = {
|
||||
if (userWallet.cardTypesResolver.isTangemWallet()) {
|
||||
router.push(
|
||||
AppRoute.ReferralProgram(userWalletId = userWallet.walletId),
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -137,5 +137,11 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
data object NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
|
||||
|
||||
data object NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
|
||||
|
||||
// region Referral Promo
|
||||
data object ReferralPromo : MainScreen(event = "Referral Banner")
|
||||
data object ReferralPromoButtonParticipate : MainScreen(event = "Button - Referral Participate")
|
||||
data object ReferralPromoButtonDismiss : MainScreen(event = "Button - Referral Dismiss")
|
||||
//endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -53,6 +53,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
source = AnalyticsParam.ScreensSources.Main,
|
||||
programName = ProgramName.Empty, // Use it on new promo action
|
||||
)
|
||||
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo
|
||||
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
|
||||
is WalletNotification.Informational.NoAccount,
|
||||
is WalletNotification.Warning.LowSignatures,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
|
||||
import com.tangem.domain.promo.models.PromoId
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -34,6 +36,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
|
||||
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
|
||||
) {
|
||||
|
||||
@Suppress("MagicNumber", "MaximumLineLength")
|
||||
|
|
@ -45,12 +48,15 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
flow2 = isReadyToShowRateAppUseCase(),
|
||||
flow3 = isNeedToBackupUseCase(userWallet.walletId),
|
||||
flow4 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
|
||||
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus ->
|
||||
flow5 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral),
|
||||
) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus, shouldShowReferralPromo ->
|
||||
buildList {
|
||||
addUsedOutdatedDataNotification(maybeTokenList)
|
||||
|
||||
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
|
||||
|
||||
addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
|
||||
|
||||
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
|
||||
|
||||
addWarningNotifications(cardTypesResolver, maybeTokenList, isNeedToBackup, clickIntents)
|
||||
|
|
@ -187,6 +193,20 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
.map(CryptoCurrencyStatus::currency)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addReferralPromoNotification(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPromo: Boolean,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.ReferralPromo(
|
||||
onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.Referral) },
|
||||
onClick = { clickIntents.onPromoClick(promoId = PromoId.Referral) },
|
||||
),
|
||||
condition = shouldShowPromo && cardTypesResolver.isTangemWallet(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addWarningNotifications(
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
tokenList: Lce<TokenListError, TokenList>,
|
||||
|
|
|
|||
|
|
@ -254,4 +254,20 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
iconResId = R.drawable.ic_error_sync_24,
|
||||
),
|
||||
)
|
||||
|
||||
data class ReferralPromo(
|
||||
val onCloseClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
) : WalletNotification(
|
||||
config = NotificationConfig(
|
||||
title = resourceReference(R.string.notification_referral_promo_title),
|
||||
subtitle = resourceReference(R.string.notification_referral_promo_text),
|
||||
iconResId = R.drawable.img_referral_promo,
|
||||
onCloseClick = onCloseClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.notification_referral_promo_button),
|
||||
onClick = onClick,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
|||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.notifications.NoteMigrationNotification
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -38,6 +39,11 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
|
|||
Notification(
|
||||
config = it.config,
|
||||
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
iconSize = if (it is WalletNotification.ReferralPromo) {
|
||||
54.dp
|
||||
} else {
|
||||
20.dp
|
||||
},
|
||||
iconTint = when (it) {
|
||||
is WalletNotification.Critical -> TangemTheme.colors.icon.warning
|
||||
is WalletNotification.Informational -> TangemTheme.colors.icon.accent
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue