diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index a23e1b705f..48b2496729 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -520,6 +520,9 @@ NFT collections Unable to load the data Choose network + Join Now + Share your code - earn 5 USDT per sale. Your friend gets 10% OFF. + Get REWARDS for every friend! Last sale price Rarity label Rarity rank diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 7bd5322685..5d1f6b11fd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -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), ) diff --git a/core/ui/src/main/res/drawable/img_referral_promo.webp b/core/ui/src/main/res/drawable/img_referral_promo.webp new file mode 100644 index 0000000000..25a0af2e67 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_referral_promo.webp differ diff --git a/data/promo/build.gradle.kts b/data/promo/build.gradle.kts index e2dd74f43c..7573a1e454 100644 --- a/data/promo/build.gradle.kts +++ b/data/promo/build.gradle.kts @@ -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) diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt index 2c409f90fd..c80337d42b 100644 --- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt +++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt @@ -14,9 +14,11 @@ 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 @@ -26,6 +28,7 @@ internal class DefaultPromoRepository( private val appPreferencesStore: AppPreferencesStore, private val promoStoriesStore: PromoStoriesStore, private val dispatchers: CoroutineDispatcherProvider, + private val referralRepository: ReferralRepository, ) : PromoRepository { private val storyContentConverter = StoryContentResponseConverter() @@ -34,7 +37,13 @@ internal class DefaultPromoRepository( 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 isReadyToShowTokenPromo(promoId: PromoId): Flow { diff --git a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt index a564ef0509..69d489a2dc 100644 --- a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt +++ b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt @@ -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, ) } } \ No newline at end of file diff --git a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt index f33ab65ce4..714e0e29dc 100644 --- a/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt +++ b/features/referral/data/src/main/java/com/tangem/feature/referral/data/ReferralRepositoryImpl.kt @@ -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, diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt index 760277f9f8..9c890ff038 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralRepository.kt @@ -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 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt index 2688e82b36..316ee31a8a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt @@ -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 @@ -70,6 +72,8 @@ internal interface WalletWarningsClickIntents { fun onClosePromoClick(promoId: PromoId) + fun onPromoClick(promoId: PromoId) + fun onSupportClick() fun onNoteMigrationButtonClick(url: String) @@ -106,6 +110,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val urlOpener: UrlOpener, private val tokensFeatureToggles: TokensFeatureToggles, private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, + private val appRouter: AppRouter, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { override fun onAddBackupCardClick() { @@ -271,17 +276,32 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( 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) { 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)) + } + } + } + override fun onSupportClick() { val scanResponse = getSelectedUserWallet()?.scanResponse ?: return val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index a07053ba04..523e8923de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -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 } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index 36ed315d4b..0db4af387d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -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, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 5ca74067c5..f50c6ea080 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -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.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.addWarningNotifications( cardTypesResolver: CardTypesResolver, tokenList: Lce, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt index 203a35a3a8..8315a590cd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt @@ -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, + ), + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index 211ce57e2d..0227a5339a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -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 TangemTheme.colors.icon.warning is WalletNotification.Informational -> TangemTheme.colors.icon.accent