diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt index 4b770200d0..2cc28a8b18 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationId.kt @@ -8,4 +8,5 @@ package com.tangem.common.ui.notifications */ enum class NotificationId(val key: String) { SendViaSwapTokenSelectorNotification("SendViaSwapTokenSelectorNotificationKey"), + EnablePushesReminderNotification("EnablePushesReminderNotification"), } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/Showcase.kt b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/Showcase.kt index 48f79e027f..e9347aa9b6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/Showcase.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/Showcase.kt @@ -122,16 +122,18 @@ fun ShowcaseButtons( private fun Showcase_Preview() { TangemThemePreview { Showcase( - headerIconRes = R.drawable.ic_notifications_unread_24, + headerIconRes = R.drawable.ic_notification_56, headerText = resourceReference(R.string.user_push_notification_agreement_header), showcaseItems = persistentListOf( ShowcaseItemModel( - R.drawable.ic_rocket_launch_24, - resourceReference(R.string.user_push_notification_agreement_argument_one), + iconRes = R.drawable.ic_rocket_launch_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_one_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_one_subtitle), ), ShowcaseItemModel( - R.drawable.ic_storefront_24, - resourceReference(R.string.user_push_notification_agreement_argument_two), + iconRes = R.drawable.ic_storefront_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_two_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_two_subtitle), ), ), primaryButton = ShowcaseButtonModel(resourceReference(R.string.common_allow), {}), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseContent.kt index 336172a208..c4ff5a39e2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseContent.kt @@ -63,7 +63,8 @@ fun ShowcaseContent( repeat(showcaseItems.size) { index -> ShowcaseItem( iconRes = showcaseItems[index].iconRes, - text = showcaseItems[index].text, + title = showcaseItems[index].title, + subtitle = showcaseItems[index].subTitle, ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseItem.kt index 2f098c6720..4fe07dc38e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/ShowcaseItem.kt @@ -1,33 +1,47 @@ package com.tangem.core.ui.components.showcase import androidx.annotation.DrawableRes -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme @Composable -internal fun ShowcaseItem(@DrawableRes iconRes: Int, text: TextReference) { - Row { +internal fun ShowcaseItem(@DrawableRes iconRes: Int, title: TextReference, subtitle: TextReference) { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { Icon( painter = painterResource(id = iconRes), contentDescription = null, tint = TangemTheme.colors.icon.primary1, ) - Text( - text = text.resolveReference(), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.secondary, + Column( modifier = Modifier .fillMaxWidth() .padding(start = TangemTheme.dimens.spacing20), - ) + verticalArrangement = Arrangement.spacedBy(3.dp), + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + modifier = Modifier.fillMaxWidth(), + ) + + Text( + text = subtitle.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + modifier = Modifier.fillMaxWidth(), + ) + } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/model/ShowcaseItemModel.kt b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/model/ShowcaseItemModel.kt index f9865aab9e..cac2da63c0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/showcase/model/ShowcaseItemModel.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/showcase/model/ShowcaseItemModel.kt @@ -5,5 +5,6 @@ import com.tangem.core.ui.extensions.TextReference data class ShowcaseItemModel( @DrawableRes val iconRes: Int, - val text: TextReference, + val title: TextReference, + val subTitle: TextReference, ) \ No newline at end of file diff --git a/core/ui/src/main/res/drawable-hdpi/img_push_reminder.webp b/core/ui/src/main/res/drawable-hdpi/img_push_reminder.webp new file mode 100644 index 0000000000..47f81ba8f4 Binary files /dev/null and b/core/ui/src/main/res/drawable-hdpi/img_push_reminder.webp differ diff --git a/core/ui/src/main/res/drawable-mdpi/img_push_reminder.webp b/core/ui/src/main/res/drawable-mdpi/img_push_reminder.webp new file mode 100644 index 0000000000..1c1df20a6f Binary files /dev/null and b/core/ui/src/main/res/drawable-mdpi/img_push_reminder.webp differ diff --git a/core/ui/src/main/res/drawable-xhdpi/img_push_reminder.webp b/core/ui/src/main/res/drawable-xhdpi/img_push_reminder.webp new file mode 100644 index 0000000000..f30b2ac133 Binary files /dev/null and b/core/ui/src/main/res/drawable-xhdpi/img_push_reminder.webp differ diff --git a/core/ui/src/main/res/drawable-xxhdpi/img_push_reminder.webp b/core/ui/src/main/res/drawable-xxhdpi/img_push_reminder.webp new file mode 100644 index 0000000000..274b3305f6 Binary files /dev/null and b/core/ui/src/main/res/drawable-xxhdpi/img_push_reminder.webp differ diff --git a/core/ui/src/main/res/drawable-xxxhdpi/img_push_reminder.webp b/core/ui/src/main/res/drawable-xxxhdpi/img_push_reminder.webp new file mode 100644 index 0000000000..fe2d4ec006 Binary files /dev/null and b/core/ui/src/main/res/drawable-xxxhdpi/img_push_reminder.webp differ diff --git a/core/ui/src/main/res/drawable/ic_notification_56.xml b/core/ui/src/main/res/drawable/ic_notification_56.xml new file mode 100644 index 0000000000..69a6c39afb --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_notification_56.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_notification_square_24.xml b/core/ui/src/main/res/drawable/ic_notification_square_24.xml new file mode 100644 index 0000000000..0a8e4b2db8 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_notification_square_24.xml @@ -0,0 +1,12 @@ + + + + diff --git a/core/ui/src/main/res/drawable/ic_stars_24.xml b/core/ui/src/main/res/drawable/ic_stars_24.xml new file mode 100644 index 0000000000..41a21a8f25 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_stars_24.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt index d386676472..7a9b4c6cbe 100644 --- a/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt +++ b/data/notifications/src/main/java/com/tangem/data/notifications/DefaultNotificationsRepository.kt @@ -2,11 +2,13 @@ package com.tangem.data.notifications import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.get import com.tangem.datasource.local.preferences.utils.getObjectMapSync import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.preferences.utils.getSyncOrNull import com.tangem.datasource.local.preferences.utils.store import com.tangem.domain.notifications.repository.NotificationsRepository +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class DefaultNotificationsRepository @Inject constructor( @@ -17,6 +19,10 @@ class DefaultNotificationsRepository @Inject constructor( return appPreferencesStore.getSyncOrDefault(PreferencesKeys.getShouldShowNotificationKey(key), true) } + override fun getShouldShowNotification(key: String): Flow { + return appPreferencesStore.get(PreferencesKeys.getShouldShowNotificationKey(key), true) + } + override suspend fun setShouldShowNotifications(key: String, value: Boolean) { appPreferencesStore.store(PreferencesKeys.getShouldShowNotificationKey(key), value) } diff --git a/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt index 0f1d3cb606..00d20b0f04 100644 --- a/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt +++ b/domain/notifications/src/main/java/com/tangem/domain/notifications/repository/NotificationsRepository.kt @@ -1,5 +1,7 @@ package com.tangem.domain.notifications.repository +import kotlinx.coroutines.flow.Flow + /** * Repository interface for managing local notification logic and state. * @@ -20,6 +22,13 @@ interface NotificationsRepository { */ suspend fun shouldShowNotification(key: String): Boolean + /** + * Subscribe on whether a notification with the given [key] should be shown to the user. + * @param key The unique identifier for the notification. + * @return true if the notification should be shown, false otherwise. + */ + fun getShouldShowNotification(key: String): Flow + /** * Sets whether a notification with the given [key] should be shown to the user. * @param key The unique identifier for the notification. diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt index 07090215d9..73d7418dfc 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt @@ -53,20 +53,18 @@ internal fun PushNotificationsContent( Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) { ShowcaseContent( - headerIconRes = R.drawable.ic_notifications_unread_24, + headerIconRes = R.drawable.ic_notification_56, headerText = resourceReference(R.string.user_push_notification_agreement_header), showcaseItems = persistentListOf( ShowcaseItemModel( - R.drawable.ic_rocket_launch_24, - resourceReference(R.string.user_push_notification_agreement_argument_one), + iconRes = R.drawable.ic_notification_square_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_one_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_one_subtitle), ), ShowcaseItemModel( - R.drawable.ic_storefront_24, - resourceReference(R.string.user_push_notification_agreement_argument_two), - ), - ShowcaseItemModel( - R.drawable.ic_notifications_24, - resourceReference(R.string.user_push_notification_agreement_argument_three), + iconRes = R.drawable.ic_stars_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_two_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_two_subtitle), ), ), modifier = Modifier.padding(top = TangemTheme.dimens.spacing40), diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt index 81ec2b2743..835e3c725a 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsScreen.kt @@ -26,20 +26,18 @@ internal fun PushNotificationsScreen( ) Showcase( - headerIconRes = R.drawable.ic_notifications_unread_24, + headerIconRes = R.drawable.ic_notification_56, headerText = resourceReference(R.string.user_push_notification_agreement_header), showcaseItems = persistentListOf( ShowcaseItemModel( - R.drawable.ic_rocket_launch_24, - resourceReference(R.string.user_push_notification_agreement_argument_one), + iconRes = R.drawable.ic_notification_square_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_one_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_one_subtitle), ), ShowcaseItemModel( - R.drawable.ic_storefront_24, - resourceReference(R.string.user_push_notification_agreement_argument_two), - ), - ShowcaseItemModel( - R.drawable.ic_notifications_24, - resourceReference(R.string.user_push_notification_agreement_argument_three), + iconRes = R.drawable.ic_stars_24, + title = resourceReference(R.string.user_push_notification_agreement_argument_two_title), + subTitle = resourceReference(R.string.user_push_notification_agreement_argument_two_subtitle), ), ), primaryButton = ShowcaseButtonModel( 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 55390d5b9e..88c0ad0300 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 @@ -1,18 +1,19 @@ package com.tangem.feature.wallet.child.wallet.model.intents +import android.os.Build import arrow.core.getOrElse import com.tangem.common.TangemBlogUrlBuilder import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter +import com.tangem.common.ui.notifications.NotificationId import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase -import com.tangem.domain.feedback.GetWalletMetaInfoUseCase import com.tangem.domain.core.wallets.UserWalletsListRepository +import com.tangem.domain.feedback.GetWalletMetaInfoUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency @@ -20,17 +21,21 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher +import com.tangem.domain.notifications.SetShouldShowNotificationUseCase +import com.tangem.domain.notifications.repository.NotificationsRepository import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.promo.ShouldShowPromoWalletUseCase import com.tangem.domain.promo.models.PromoId import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher import com.tangem.domain.settings.NeverToSuggestRateAppUseCase import com.tangem.domain.settings.RemindToRateAppLaterUseCase +import com.tangem.domain.settings.repositories.PermissionRepository import com.tangem.domain.staking.StakingIdFactory import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType import com.tangem.domain.wallets.models.UnlockWalletsError +import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase @@ -54,6 +59,7 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +@Suppress("TooManyFunctions") internal interface WalletWarningsClickIntents { fun onAddBackupCardClick() @@ -93,9 +99,13 @@ internal interface WalletWarningsClickIntents { fun onSeedPhraseSecondNotificationReject() fun onFinishWalletActivationClick() + + fun onAllowPermissions() + + fun onDenyPermissions() } -@Suppress("LargeClass", "LongParameterList") +@Suppress("LargeClass", "LongParameterList", "TooManyFunctions") @ModelScoped internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val stateHolder: WalletStateController, @@ -121,6 +131,9 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( private val appRouter: AppRouter, private val hotWalletFeatureToggles: HotWalletFeatureToggles, private val userWalletsListRepository: UserWalletsListRepository, + private val setShouldShowNotificationUseCase: SetShouldShowNotificationUseCase, + private val notificationsRepository: NotificationsRepository, + private val permissionRepository: PermissionRepository, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { override fun onAddBackupCardClick() { @@ -422,6 +435,39 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor( appRouter.push(AppRoute.WalletActivation(userWallet.walletId)) } + override fun onAllowPermissions() { + if (isNotificationsPermissionGranted()) { + updateSubscribeOnPushPermissions(true) + } else { + walletEventSender.send( + event = WalletEvent.RequestPushPermissions( + onAllow = { updateSubscribeOnPushPermissions(true) }, + onDeny = { updateSubscribeOnPushPermissions(false) }, + ), + ) + } + } + + override fun onDenyPermissions() = updateSubscribeOnPushPermissions(false) + + private fun updateSubscribeOnPushPermissions(shouldAllow: Boolean) { + modelScope.launch { + notificationsRepository.setUserAllowToSubscribeOnPushNotifications(shouldAllow) + setShouldShowNotificationUseCase( + key = NotificationId.EnablePushesReminderNotification.key, + value = false, + ) + } + } + + private fun isNotificationsPermissionGranted(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + permissionRepository.hasRuntimePermission(android.Manifest.permission.POST_NOTIFICATIONS) + } else { + true + } + } + private suspend fun fetchCryptoCurrencies(userWalletId: UserWalletId, currencies: List) { coroutineScope { listOf( 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 9228228cd4..62e98b08ac 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 @@ -4,7 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped -import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent +import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.* import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.Program import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification @@ -49,11 +49,11 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem is WalletNotification.Critical.BackupError -> MainScreen.BackupError is WalletNotification.NoteMigration -> MainScreen.NotePromo - is WalletNotification.SwapPromo -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner( + is WalletNotification.SwapPromo -> NoticePromotionBanner( source = AnalyticsParam.ScreensSources.Main, program = Program.Empty, // Use it on new promo action ) - is WalletNotification.Sepa -> TokenSwapPromoAnalyticsEvent.NoticePromotionBanner( + is WalletNotification.Sepa -> NoticePromotionBanner( source = AnalyticsParam.ScreensSources.Main, program = Program.Sepa, ) @@ -70,6 +70,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( -> null is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport is WalletNotification.Critical.SeedPhraseSecondNotification -> MainScreen.NoticeSeedPhraseSupportSecond + is WalletNotification.PushNotifications -> null } } } \ No newline at end of file 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 8243fa0635..fa7d183dce 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 @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain import arrow.core.getOrElse +import com.tangem.common.ui.notifications.NotificationId import com.tangem.common.ui.userwallet.ext.walletInterationIcon import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState @@ -18,6 +19,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.notifications.repository.NotificationsRepository import com.tangem.domain.onramp.GetOnrampCountryUseCase import com.tangem.domain.onramp.OnrampSepaAvailableUseCase import com.tangem.domain.promo.ShouldShowPromoWalletUseCase @@ -33,16 +35,16 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles import com.tangem.lib.crypto.BlockchainUtils.isBitcoin -import com.tangem.utils.coroutines.combine6 import com.tangem.utils.extensions.addIf import com.tangem.utils.extensions.isPositive import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import timber.log.Timber import javax.inject.Inject -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") @ModelScoped internal class GetMultiWalletWarningsFactory @Inject constructor( private val tokenListStore: MultiWalletTokenListStore, @@ -55,20 +57,31 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase, private val onrampSepaAvailableUseCase: OnrampSepaAvailableUseCase, private val getOnrampCountryUseCase: GetOnrampCountryUseCase, + private val notificationsRepository: NotificationsRepository, private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles, ) { + @Suppress("UNCHECKED_CAST", "MagicNumber") fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow> { val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver - return combine6( - flow1 = tokenListStore.getOrThrow(userWallet.walletId), - flow2 = isReadyToShowRateAppUseCase(), - flow3 = isNeedToBackupUseCase(userWallet.walletId), - flow4 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId), - flow5 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral), - flow6 = shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa), - ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, seedPhraseIssueStatus, shouldShowReferralPromo, shouldShowSepaBanner -> + return combine( + tokenListStore.getOrThrow(userWallet.walletId), + isReadyToShowRateAppUseCase(), + isNeedToBackupUseCase(userWallet.walletId), + seedPhraseNotificationUseCase(userWalletId = userWallet.walletId), + shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Referral), + shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa), + notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key), + ) { array -> + val maybeTokenList = array[0] as Lce + val isReadyToShowRating = array[1] as Boolean + val isNeedToBackup = array[2] as Boolean + val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus + val shouldShowReferralPromo = array[4] as Boolean + val shouldShowSepaBanner = array[5] as Boolean + val shouldShowEnablePushesReminderNotification = array[6] as Boolean + buildList { addUsedOutdatedDataNotification(maybeTokenList) @@ -84,6 +97,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( addWarningNotifications(cardTypesResolver, maybeTokenList, isNeedToBackup, clickIntents) + addPushReminderNotification( + clickIntents = clickIntents, + shouldShowPushReminderBanner = shouldShowEnablePushesReminderNotification && + !notificationsRepository.isUserAllowToSubscribeOnPushNotifications(), + ) + addYieldSupplyNotifications(maybeTokenList) val hasCriticalOrWarning = any { notification -> @@ -305,6 +324,19 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( ) } + private fun MutableList.addPushReminderNotification( + clickIntents: WalletClickIntents, + shouldShowPushReminderBanner: Boolean, + ) { + addIf( + element = WalletNotification.PushNotifications( + onCloseClick = clickIntents::onDenyPermissions, + onEnabledClick = clickIntents::onAllowPermissions, + ), + condition = shouldShowPushReminderBanner, + ) + } + private fun Lce.hasUnreachableNetworks(): Boolean { val tokenList = getOrNull(isPartialContentAccepted = false) ?: return false diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt index 662fdca3de..b3f535d8a2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt @@ -30,4 +30,9 @@ internal sealed class WalletEvent { RIGHT, } } + + data class RequestPushPermissions( + val onAllow: () -> Unit, + val onDeny: () -> Unit, + ) : WalletEvent() } \ No newline at end of file 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 a9f3f1beb1..bc42b5dc0b 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 @@ -315,4 +315,23 @@ sealed class WalletNotification(val config: NotificationConfig) { iconSize = 54.dp, ), ) + + data class PushNotifications( + val onCloseClick: () -> Unit, + val onEnabledClick: () -> Unit, + ) : WalletNotification( + config = NotificationConfig( + title = resourceReference(R.string.user_push_notification_banner_title), + subtitle = resourceReference(R.string.user_push_notification_banner_subtitle), + iconResId = R.drawable.img_push_reminder, + onCloseClick = onCloseClick, + buttonsState = NotificationConfig.ButtonsState.PairButtonsConfig( + primaryText = resourceReference(id = R.string.common_enable), + onPrimaryClick = onEnabledClick, + secondaryText = resourceReference(id = R.string.common_later), + onSecondaryClick = onCloseClick, + ), + iconSize = 54.dp, + ), + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt index 6553a234d3..f1e5840afa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt @@ -3,19 +3,20 @@ package com.tangem.feature.wallet.presentation.wallet.ui import androidx.compose.foundation.lazy.LazyListState import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.* import androidx.compose.ui.platform.LocalContext import com.tangem.core.res.getStringSafe import com.tangem.core.ui.event.EventEffect import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.utils.requestPermission import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.ui.utils.ReviewManagerRequester import com.tangem.feature.wallet.presentation.wallet.ui.utils.animateScrollByIndex import com.tangem.feature.wallet.presentation.wallet.ui.utils.demonstrateScrolling +import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION @Composable internal fun WalletEventEffect( @@ -28,6 +29,13 @@ internal fun WalletEventEffect( val coroutineScope = rememberCoroutineScope() val context = LocalContext.current val resources = LocalContext.current.resources + + var showPermissionRequest by remember { mutableStateOf Unit, () -> Unit>?>(null) } + HandlePermissionRequest( + permissionRequestParams = showPermissionRequest, + onPermissionRequestResult = { showPermissionRequest = null }, + ) + EventEffect( event = event, onTrigger = { value -> @@ -56,7 +64,34 @@ internal fun WalletEventEffect( is WalletEvent.DemonstrateWalletsScrollPreview -> { walletsListState.demonstrateScrolling(coroutineScope = coroutineScope, direction = value.direction) } + is WalletEvent.RequestPushPermissions -> { + showPermissionRequest = value.onAllow to value.onDeny + } } }, ) +} + +@Composable +private fun HandlePermissionRequest( + permissionRequestParams: Pair<() -> Unit, () -> Unit>?, + onPermissionRequestResult: () -> Unit, +) { + val actualPermissionLauncher = permissionRequestParams?.let { (onAllowExternal, onDenyExternal) -> + requestPermission( + onAllow = { + onAllowExternal() + onPermissionRequestResult() + }, + onDeny = { + onDenyExternal() + onPermissionRequestResult() + }, + permission = PUSH_PERMISSION, + ) + } + + LaunchedEffect(permissionRequestParams) { + actualPermissionLauncher?.invoke() + } } \ No newline at end of file