From 9ef9542750ebb5275b210ee91f1b51824d7c48a7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 29 Jul 2025 15:09:21 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../FirebasePushNotificationsTokenProvider.kt | 8 +- .../tangem/tap/routing/utils/ChildFactory.kt | 6 +- .../local/preferences/PreferencesKeys.kt | 12 ++ .../core/ui/components/showcase/Showcase.kt | 2 +- .../DefaultNotificationsRepository.kt | 32 +++++ .../repository/NotificationsRepository.kt | 10 ++ ...letsForAutomaticallyPushEnablingUseCase.kt | 27 ++++ features/disclaimer/impl/build.gradle.kts | 1 + .../disclaimer/impl/entity/DisclaimerUM.kt | 2 +- .../disclaimer/impl/model/DisclaimerModel.kt | 6 +- .../disclaimer/impl/ui/DisclaimerScreen.kt | 12 +- .../PushNotificationsBottomSheetComponent.kt | 9 ++ .../api/PushNotificationsComponent.kt | 2 +- .../api/PushNotificationsModelCallbacks.kt | 17 +++ .../api/PushNotificationsParams.kt | 6 + .../push-notifications/impl/build.gradle.kts | 3 + ...ltPushNotificationsBottomSheetComponent.kt | 62 +++++++++ .../impl/DefaultPushNotificationsComponent.kt | 12 +- .../impl/di/PushNotificationsModule.kt | 7 ++ .../model/PushNotificationsClickIntents.kt | 4 +- .../impl/model/PushNotificationsModel.kt | 37 +++++- .../ui/PushNotificationsBottomSheet.kt | 118 ++++++++++++++++++ .../ui/PushNotificationsScreen.kt | 10 +- .../model/WalletSettingsModel.kt | 3 + features/wallet/impl/build.gradle.kts | 2 + .../wallet/child/wallet/WalletComponent.kt | 10 ++ .../wallet/child/wallet/model/WalletModel.kt | 73 +++++++++-- .../wallet/state/model/WalletDialogConfig.kt | 3 + .../presentation/wallet/ui/WalletScreen.kt | 2 - .../PushNotificationsBottomSheet.kt | 111 ---------------- 30 files changed, 452 insertions(+), 157 deletions(-) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsForAutomaticallyPushEnablingUseCase.kt create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsBottomSheetComponent.kt create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsModelCallbacks.kt create mode 100644 features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsBottomSheetComponent.kt create mode 100644 features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt diff --git a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt index 6167cea5a0..358bdd3d61 100644 --- a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt +++ b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt @@ -3,10 +3,16 @@ package com.tangem.tap.data import com.google.firebase.messaging.FirebaseMessaging import com.tangem.utils.notifications.PushNotificationsTokenProvider import kotlinx.coroutines.tasks.await +import timber.log.Timber import javax.inject.Inject internal class FirebasePushNotificationsTokenProvider @Inject constructor() : PushNotificationsTokenProvider { override suspend fun getToken(): String { - return FirebaseMessaging.getInstance().token.await() + return try { + FirebaseMessaging.getInstance().token.await() + } catch (ex: Exception) { + Timber.e(ex) + "" + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 825a388842..88711a8d52 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -22,6 +22,8 @@ import com.tangem.features.nft.component.NFTComponent import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent import com.tangem.features.onramp.component.* import com.tangem.features.pushnotifications.api.PushNotificationsComponent +import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacksStub +import com.tangem.features.pushnotifications.api.PushNotificationsParams import com.tangem.features.send.v2.api.NFTSendComponent import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.staking.api.StakingComponent @@ -370,7 +372,9 @@ internal class ChildFactory @Inject constructor( is AppRoute.PushNotification -> { createComponentChild( context = context, - params = Unit, + params = PushNotificationsParams( + modelCallbacks = PushNotificationsModelCallbacksStub(), + ), componentFactory = pushNotificationsComponentFactory, ) } diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 874a358083..d1b906f30e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -134,6 +134,18 @@ object PreferencesKeys { val NOTIFICATIONS_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "notificationsEnabledStates") } + val NOTIFICATIONS_AUTOMATICALLY_ENABLED_STATES_KEY by lazy { + stringPreferencesKey( + name = "notificationsAutomaticallyEnabledStates", + ) + } + + val NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY by lazy { + booleanPreferencesKey( + name = "userAllowSendAddresses", + ) + } + val TRON_NETWORK_FEE_NOTIFICATION_SHOW_COUNT_KEY by lazy { intPreferencesKey(name = "tronNetworkFeeNotificationShowCount") } 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 8112024b54..48f79e027f 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 @@ -66,7 +66,7 @@ fun Showcase( } @Composable -private fun ShowcaseButtons( +fun ShowcaseButtons( primaryButtonText: TextReference, secondaryButtonText: TextReference, onPrimaryClick: () -> Unit, 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 d1f92e8262..45d62bced2 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 @@ -82,4 +82,36 @@ internal class DefaultNotificationsRepository @Inject constructor( NotificationsEligibleNetworkConverter.convert(it) } } + + override suspend fun shouldShowSubscribeOnNotificationsAfterUpdate(): Boolean { + return appPreferencesStore.getSyncOrNull( + key = PreferencesKeys.NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY, + ) == null + } + + override suspend fun isUserAllowToSubscribeOnPushNotifications(): Boolean { + return appPreferencesStore.getSyncOrDefault( + key = PreferencesKeys.NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY, + default = false, + ) + } + + override suspend fun setUserAllowToSubscribeOnPushNotifications(value: Boolean) { + appPreferencesStore.store(PreferencesKeys.NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY, value) + } + + override suspend fun getWalletAutomaticallyEnabledList(): List = appPreferencesStore + .getObjectMapSync(PreferencesKeys.NOTIFICATIONS_AUTOMATICALLY_ENABLED_STATES_KEY).map { + it.key + } + + override suspend fun setNotificationsWasEnabledAutomatically(userWalletId: String) { + appPreferencesStore.editData { + it.setObjectMap( + key = PreferencesKeys.NOTIFICATIONS_AUTOMATICALLY_ENABLED_STATES_KEY, + value = it.getObjectMap(PreferencesKeys.NOTIFICATIONS_AUTOMATICALLY_ENABLED_STATES_KEY) + .plus(userWalletId to true), + ) + } + } } \ No newline at end of file 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 a8c46854df..59f5e6e58b 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 @@ -21,4 +21,14 @@ interface NotificationsRepository { @Throws suspend fun getEligibleNetworks(): List + + suspend fun shouldShowSubscribeOnNotificationsAfterUpdate(): Boolean + + suspend fun isUserAllowToSubscribeOnPushNotifications(): Boolean + + suspend fun setUserAllowToSubscribeOnPushNotifications(value: Boolean) + + suspend fun getWalletAutomaticallyEnabledList(): List + + suspend fun setNotificationsWasEnabledAutomatically(userWalletId: String) } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsForAutomaticallyPushEnablingUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsForAutomaticallyPushEnablingUseCase.kt new file mode 100644 index 0000000000..9ba38eabe0 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetWalletsForAutomaticallyPushEnablingUseCase.kt @@ -0,0 +1,27 @@ +package com.tangem.domain.wallets.usecase + +import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import javax.inject.Inject + +/** + * Use case for retrieving wallets where automatically enabling push notifications was not applied. + * * This use case filters out wallets that have already had push notifications automatically enabled + * from the complete list of user wallets, returning only those wallets that still need to have + * push notifications automatically enabled. + * * @property userWalletsListManager Manager for user wallets list operations + * @property dispatchers Coroutine dispatcher provider for background operations + */ +class GetWalletsForAutomaticallyPushEnablingUseCase @Inject constructor( + private val userWalletsListManager: UserWalletsListManager, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke(walletsListWherePushWasEnabled: List): List = + withContext(dispatchers.default) { + val allLocalWallets = userWalletsListManager.userWalletsSync.map { it.walletId } + allLocalWallets - walletsListWherePushWasEnabled.toSet() + } +} \ No newline at end of file diff --git a/features/disclaimer/impl/build.gradle.kts b/features/disclaimer/impl/build.gradle.kts index 9144b3fbb0..3454262136 100644 --- a/features/disclaimer/impl/build.gradle.kts +++ b/features/disclaimer/impl/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.card) implementation(projects.domain.settings) + implementation(projects.domain.notifications) /** Feature modules */ implementation(projects.features.disclaimer.api) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/entity/DisclaimerUM.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/entity/DisclaimerUM.kt index 155a91269b..4e15c1b3dd 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/entity/DisclaimerUM.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/entity/DisclaimerUM.kt @@ -3,7 +3,7 @@ package com.tangem.features.disclaimer.impl.entity internal data class DisclaimerUM( val url: String, val isTosAccepted: Boolean, - val onAccept: (Boolean) -> Unit, + val onAccept: () -> Unit, val popBack: () -> Unit, ) diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt index f96cb66625..39b9bde62b 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/model/DisclaimerModel.kt @@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.navigation.finisher.AppFinisher import com.tangem.domain.card.repository.CardRepository +import com.tangem.domain.notifications.repository.NotificationsRepository import com.tangem.domain.settings.NeverRequestPermissionUseCase import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase import com.tangem.features.disclaimer.api.components.DisclaimerComponent @@ -26,6 +27,7 @@ internal class DisclaimerModel @Inject constructor( private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase, private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase, private val appFinisher: AppFinisher, + private val notificationsRepository: NotificationsRepository, paramsContainer: ParamsContainer, ) : Model() { @@ -40,12 +42,12 @@ internal class DisclaimerModel @Inject constructor( ), ) - private fun onAccept(shouldAskPushPermission: Boolean) = modelScope.launch { + private fun onAccept() = modelScope.launch { if (params.isTosAccepted) { router.pop() } else { cardRepository.acceptTangemTOS() - + val shouldAskPushPermission = notificationsRepository.shouldShowSubscribeOnNotificationsAfterUpdate() if (shouldAskPushPermission) { router.push(AppRoute.PushNotification) } else { diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt index 1174e7d634..ef34536252 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/ui/DisclaimerScreen.kt @@ -15,9 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.tooling.preview.Preview -import com.google.accompanist.permissions.ExperimentalPermissionsApi -import com.google.accompanist.permissions.isGranted -import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.web.WebView import com.google.accompanist.web.WebViewNavigator import com.google.accompanist.web.rememberWebViewNavigator @@ -39,7 +36,6 @@ import com.tangem.features.disclaimer.impl.R import com.tangem.features.disclaimer.impl.entity.DisclaimerUM import com.tangem.features.disclaimer.impl.entity.DummyDisclaimer import com.tangem.features.disclaimer.impl.local.localTermsOfServices -import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.withDebounce import java.nio.charset.StandardCharsets @@ -167,15 +163,11 @@ private fun WebViewNavigator.loadLocalToS() { ) } -@OptIn(ExperimentalPermissionsApi::class) @Composable -private fun BoxScope.DisclaimerButton(onAccept: (Boolean) -> Unit) { - val isPermissionGranted = getPushPermissionOrNull()?.let { permission -> - rememberPermissionState(permission = permission).status.isGranted - } ?: true +private fun BoxScope.DisclaimerButton(onAccept: () -> Unit) { PrimaryButton( text = stringResourceSafe(id = R.string.common_accept), - onClick = { onAccept(!isPermissionGranted) }, + onClick = onAccept, colors = ButtonColors( containerColor = TangemColorPalette.Light4, contentColor = TangemColorPalette.Dark6, diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsBottomSheetComponent.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsBottomSheetComponent.kt new file mode 100644 index 0000000000..b72b8c418c --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsBottomSheetComponent.kt @@ -0,0 +1,9 @@ +package com.tangem.features.pushnotifications.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent + +interface PushNotificationsBottomSheetComponent : ComposableBottomSheetComponent { + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsComponent.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsComponent.kt index 515492abe3..a514eb89a5 100644 --- a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsComponent.kt +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsComponent.kt @@ -5,5 +5,5 @@ import com.tangem.core.ui.decompose.ComposableContentComponent interface PushNotificationsComponent : ComposableContentComponent { - interface Factory : ComponentFactory + interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsModelCallbacks.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsModelCallbacks.kt new file mode 100644 index 0000000000..60582b60df --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsModelCallbacks.kt @@ -0,0 +1,17 @@ +package com.tangem.features.pushnotifications.api + +interface PushNotificationsModelCallbacks { + fun onAllowSystemPermission() + fun onDenySystemPermission() + fun onDismiss() +} + +class PushNotificationsModelCallbacksStub( + val onAllowSystemPermission: () -> Unit = {}, + val onDenySystemPermission: () -> Unit = {}, + val onDismiss: () -> Unit = {}, +) : PushNotificationsModelCallbacks { + override fun onAllowSystemPermission() = onAllowSystemPermission.invoke() + override fun onDenySystemPermission() = onDenySystemPermission.invoke() + override fun onDismiss() = onDismiss.invoke() +} \ No newline at end of file diff --git a/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt new file mode 100644 index 0000000000..27bc4d7131 --- /dev/null +++ b/features/push-notifications/api/src/main/java/com/tangem/features/pushnotifications/api/PushNotificationsParams.kt @@ -0,0 +1,6 @@ +package com.tangem.features.pushnotifications.api + +data class PushNotificationsParams( + val isBottomSheet: Boolean = false, + val modelCallbacks: PushNotificationsModelCallbacks, +) \ No newline at end of file diff --git a/features/push-notifications/impl/build.gradle.kts b/features/push-notifications/impl/build.gradle.kts index 68fc33bd13..2f2c875648 100644 --- a/features/push-notifications/impl/build.gradle.kts +++ b/features/push-notifications/impl/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { implementation(deps.androidx.activity.compose) /** Compose */ + implementation(deps.compose.material3) + implementation(deps.compose.ui.tooling) implementation(deps.compose.foundation) implementation(deps.compose.accompanist.systemUiController) implementation(deps.compose.accompanist.permission) @@ -39,6 +41,7 @@ dependencies { /** Domain module */ implementation(projects.domain.settings) implementation(projects.domain.notifications.toggles) + implementation(projects.domain.notifications) /** Feature modules */ implementation(projects.features.pushNotifications.api) diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsBottomSheetComponent.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsBottomSheetComponent.kt new file mode 100644 index 0000000000..259b0bacf0 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsBottomSheetComponent.kt @@ -0,0 +1,62 @@ +package com.tangem.features.pushnotifications.impl + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent +import com.tangem.features.pushnotifications.api.PushNotificationsParams +import com.tangem.features.pushnotifications.impl.model.PushNotificationsModel +import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsBottomSheet +import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsContent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultPushNotificationsBottomSheetComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted private val params: PushNotificationsParams, +) : PushNotificationsBottomSheetComponent, AppComponentContext by appComponentContext { + + private val model: PushNotificationsModel = getOrCreateModel(params) + + @AssistedFactory + interface Factory : PushNotificationsBottomSheetComponent.Factory { + override fun create( + context: AppComponentContext, + params: PushNotificationsParams, + ): DefaultPushNotificationsBottomSheetComponent + } + + override fun dismiss() { + params.modelCallbacks.onDismiss() + } + + @Composable + override fun BottomSheet() { + val state by model.state.collectAsState() + val bottomSheetConfig = remember(key1 = this) { + TangemBottomSheetConfig( + isShown = true, + onDismissRequest = ::dismiss, + content = TangemBottomSheetConfigContent.Empty, + ) + } + + PushNotificationsBottomSheet( + config = bottomSheetConfig, + ) { + PushNotificationsContent( + onAllowClick = model::onAllowClick, + onLaterClick = model::onLaterClick, + onAllowPermission = model::onAllowPermission, + onDenyPermission = model::onDenyPermission, + showNotificationsInfo = state.showInfoAboutNotifications, + ) + } + } +} \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsComponent.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsComponent.kt index 07278cd29d..458d977d4e 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsComponent.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/DefaultPushNotificationsComponent.kt @@ -11,6 +11,7 @@ import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.utils.findActivity import com.tangem.features.pushnotifications.api.PushNotificationsComponent +import com.tangem.features.pushnotifications.api.PushNotificationsParams import com.tangem.features.pushnotifications.impl.model.PushNotificationsModel import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsScreen import dagger.assisted.Assisted @@ -19,7 +20,7 @@ import dagger.assisted.AssistedInject internal class DefaultPushNotificationsComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, - @Assisted params: Unit, + @Assisted private val params: PushNotificationsParams, ) : PushNotificationsComponent, AppComponentContext by appComponentContext { private val model: PushNotificationsModel = getOrCreateModel(params) @@ -31,8 +32,8 @@ internal class DefaultPushNotificationsComponent @AssistedInject constructor( BackHandler(onBack = { activity.finish() }) NavigationBar3ButtonsScrim() PushNotificationsScreen( - onRequest = model::onRequest, - onNeverRequest = model::onNeverRequest, + onAllowClick = model::onAllowClick, + onLaterClick = model::onLaterClick, onAllowPermission = model::onAllowPermission, onDenyPermission = model::onDenyPermission, showNotificationsInfo = state.showInfoAboutNotifications, @@ -41,6 +42,9 @@ internal class DefaultPushNotificationsComponent @AssistedInject constructor( @AssistedFactory interface Factory : PushNotificationsComponent.Factory { - override fun create(context: AppComponentContext, params: Unit): DefaultPushNotificationsComponent + override fun create( + context: AppComponentContext, + params: PushNotificationsParams, + ): DefaultPushNotificationsComponent } } \ No newline at end of file diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt index edbd3dc188..06d4376447 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/di/PushNotificationsModule.kt @@ -1,7 +1,9 @@ package com.tangem.features.pushnotifications.impl.di import com.tangem.core.decompose.model.Model +import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent import com.tangem.features.pushnotifications.api.PushNotificationsComponent +import com.tangem.features.pushnotifications.impl.DefaultPushNotificationsBottomSheetComponent import com.tangem.features.pushnotifications.impl.DefaultPushNotificationsComponent import com.tangem.features.pushnotifications.impl.model.PushNotificationsModel import dagger.Binds @@ -15,6 +17,11 @@ import dagger.multibindings.IntoMap @InstallIn(SingletonComponent::class) internal interface PushNotificationsModule { + @Binds + fun bindBottomSheetComponentFactory( + impl: DefaultPushNotificationsBottomSheetComponent.Factory, + ): PushNotificationsBottomSheetComponent.Factory + @Binds fun bindComponentFactory(impl: DefaultPushNotificationsComponent.Factory): PushNotificationsComponent.Factory diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsClickIntents.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsClickIntents.kt index 99b02a379c..ad7b69bc71 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsClickIntents.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsClickIntents.kt @@ -1,9 +1,9 @@ package com.tangem.features.pushnotifications.impl.model internal interface PushNotificationsClickIntents { - fun onRequest() + fun onAllowClick() - fun onNeverRequest() + fun onLaterClick() fun onAllowPermission() diff --git a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt index de2d442132..ebf625c1ee 100644 --- a/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/model/PushNotificationsModel.kt @@ -7,9 +7,12 @@ 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.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.notifications.repository.NotificationsRepository import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles import com.tangem.domain.settings.NeverRequestPermissionUseCase import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase +import com.tangem.features.pushnotifications.api.PushNotificationsParams import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.impl.presentation.state.PushNotificationsUM @@ -19,17 +22,22 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject +@Suppress("LongParameterList") @Stable @ModelScoped internal class PushNotificationsModel @Inject constructor( + paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase, private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase, private val appRouter: AppRouter, private val analyticHandler: AnalyticsEventHandler, private val notificationsFeatureToggles: NotificationsFeatureToggles, + private val notificationsRepository: NotificationsRepository, ) : Model(), PushNotificationsClickIntents { + val params: PushNotificationsParams = paramsContainer.require() + private val _state = MutableStateFlow( PushNotificationsUM( showInfoAboutNotifications = notificationsFeatureToggles.isNotificationsEnabled, @@ -38,20 +46,33 @@ internal class PushNotificationsModel @Inject constructor( val state = _state.asStateFlow() - override fun onRequest() { + override fun onAllowClick() { + if (notificationsFeatureToggles.isNotificationsEnabled) { + modelScope.launch { + notificationsRepository.setUserAllowToSubscribeOnPushNotifications(true) + } + } analyticHandler.send( PushNotificationAnalyticEvents.ButtonAllow(AnalyticsParam.ScreensSources.Stories), ) } - override fun onNeverRequest() { + override fun onLaterClick() { + if (notificationsFeatureToggles.isNotificationsEnabled) { + modelScope.launch { + notificationsRepository.setUserAllowToSubscribeOnPushNotifications(false) + } + } analyticHandler.send( PushNotificationAnalyticEvents.ButtonLater(AnalyticsParam.ScreensSources.Stories), ) modelScope.launch { neverRequestPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) - appRouter.push(AppRoute.Home) + params.modelCallbacks.onDenySystemPermission() + if (!params.isBottomSheet) { + appRouter.push(AppRoute.Home) + } } } @@ -62,7 +83,10 @@ internal class PushNotificationsModel @Inject constructor( modelScope.launch { neverRequestPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) - appRouter.push(AppRoute.Home) + params.modelCallbacks.onAllowSystemPermission() + if (!params.isBottomSheet) { + appRouter.push(AppRoute.Home) + } } } @@ -73,7 +97,10 @@ internal class PushNotificationsModel @Inject constructor( modelScope.launch { neverRequestPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) - appRouter.push(AppRoute.Home) + params.modelCallbacks.onDenySystemPermission() + if (!params.isBottomSheet) { + appRouter.push(AppRoute.Home) + } } } } \ No newline at end of file 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 new file mode 100644 index 0000000000..bd30c68bc5 --- /dev/null +++ b/features/push-notifications/impl/src/main/java/com/tangem/features/pushnotifications/impl/presentation/ui/PushNotificationsBottomSheet.kt @@ -0,0 +1,118 @@ +package com.tangem.features.pushnotifications.impl.presentation.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.R +import com.tangem.core.ui.components.SpacerH28 +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.components.showcase.ShowcaseButtons +import com.tangem.core.ui.components.showcase.ShowcaseContent +import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.utils.requestPushPermission +import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig, content: @Composable () -> Unit) { + TangemModalBottomSheet( + config = config, + title = { + TangemModalBottomSheetTitle( + endIconRes = R.drawable.ic_close_24, + onEndClick = config.onDismissRequest, + ) + }, + ) { + content() + } +} + +@Composable +internal fun PushNotificationsContent( + onAllowClick: () -> Unit, + onLaterClick: () -> Unit, + onAllowPermission: () -> Unit, + onDenyPermission: () -> Unit, + showNotificationsInfo: Boolean, +) { + val requestPushPermission = requestPushPermission( + onAllow = onAllowPermission, + onDeny = onDenyPermission, + pushPermission = getPushPermissionOrNull(), + ) + + Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) { + ShowcaseContent( + headerIconRes = R.drawable.ic_notifications_unread_24, + 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), + ), + ShowcaseItemModel( + R.drawable.ic_storefront_24, + resourceReference(R.string.user_push_notification_agreement_argument_two), + ), + ).let { baseItems -> + if (showNotificationsInfo) { + baseItems.add( + ShowcaseItemModel( + R.drawable.ic_notifications_24, + resourceReference(R.string.user_push_notification_agreement_argument_three), + ), + ) + } else { + baseItems + } + }, + modifier = Modifier.padding(top = TangemTheme.dimens.spacing40), + ) + SpacerH28() + ShowcaseButtons( + primaryButtonText = resourceReference(R.string.common_allow), + onPrimaryClick = { + onAllowClick() + requestPushPermission() + }, + secondaryButtonText = resourceReference(R.string.common_later), + onSecondaryClick = { + onLaterClick() + }, + ) + } +} + +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun Preview_PushNotificationsBottomSheet() { + TangemThemePreview { + PushNotificationsBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = {}, + content = TangemBottomSheetConfigContent.Empty, + ), + ) { + PushNotificationsContent( + onAllowClick = {}, + onLaterClick = {}, + onAllowPermission = {}, + onDenyPermission = {}, + showNotificationsInfo = true, + ) + } + } +} \ No newline at end of file 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 4d6a0e3406..207ff60b54 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 @@ -14,8 +14,8 @@ import kotlinx.collections.immutable.persistentListOf @Composable internal fun PushNotificationsScreen( - onRequest: () -> Unit, - onNeverRequest: () -> Unit, + onAllowClick: () -> Unit, + onLaterClick: () -> Unit, onAllowPermission: () -> Unit, onDenyPermission: () -> Unit, showNotificationsInfo: Boolean, @@ -53,13 +53,15 @@ internal fun PushNotificationsScreen( primaryButton = ShowcaseButtonModel( buttonText = resourceReference(R.string.common_allow), onClick = { - onRequest() + onAllowClick() requestPushPermission() }, ), secondaryButton = ShowcaseButtonModel( buttonText = resourceReference(R.string.common_later), - onClick = onNeverRequest, + onClick = { + onLaterClick() + }, ), modifier = Modifier.systemBarsPadding(), ) diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt index b85bb4de06..4b62a5e4d7 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/model/WalletSettingsModel.kt @@ -24,6 +24,7 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.nft.DisableWalletNFTUseCase import com.tangem.domain.nft.EnableWalletNFTUseCase import com.tangem.domain.nft.GetWalletNFTEnabledUseCase +import com.tangem.domain.notifications.repository.NotificationsRepository import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles import com.tangem.domain.settings.repositories.PermissionRepository import com.tangem.domain.wallets.models.UserWallet @@ -72,6 +73,7 @@ internal class WalletSettingsModel @Inject constructor( private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase, private val settingsManager: SettingsManager, private val permissionsRepository: PermissionRepository, + private val notificationsRepository: NotificationsRepository, ) : Model() { val params: WalletSettingsComponent.Params = paramsContainer.require() @@ -258,6 +260,7 @@ internal class WalletSettingsModel @Inject constructor( if (isGranted) { modelScope.launch { setNotificationsEnabledUseCase(params.userWalletId, true).onRight { + notificationsRepository.setNotificationsWasEnabledAutomatically(params.userWalletId.stringValue) analyticsEventHandler.send(PushNotificationAnalyticEvents.NotificationsEnabled(true)) } } diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index a81c3a07e1..df82732dcb 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -96,6 +96,8 @@ dependencies { implementation(projects.domain.visa) implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) + implementation(projects.domain.notifications) + implementation(projects.domain.notifications.toggles) /** Feature Apis */ implementation(projects.features.details.api) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt index 21400eff17..07a629a880 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt @@ -21,6 +21,8 @@ import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.walletsettings.component.RenameWalletComponent import com.tangem.features.biometry.AskBiometryComponent import com.tangem.features.markets.entry.MarketsEntryComponent +import com.tangem.features.pushnotifications.api.PushNotificationsBottomSheetComponent +import com.tangem.features.pushnotifications.api.PushNotificationsParams import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -32,6 +34,7 @@ internal class WalletComponent @AssistedInject constructor( private val renameWalletComponentFactory: RenameWalletComponent.Factory, private val marketsEntryComponentFactory: MarketsEntryComponent.Factory, private val askBiometryComponentFactory: AskBiometryComponent.Factory, + private val pushNotificationsBottomSheetComponent: PushNotificationsBottomSheetComponent.Factory, ) : ComposableContentComponent, AppComponentContext by appComponentContext { private val model: WalletModel = getOrCreateModel() @@ -66,6 +69,13 @@ internal class WalletComponent @AssistedInject constructor( ), ) } + WalletDialogConfig.AskForPushNotifications -> pushNotificationsBottomSheetComponent.create( + context = childByContext(componentContext), + params = PushNotificationsParams( + isBottomSheet = true, + modelCallbacks = model.askForPushNotificationsModelCallbacks, + ), + ) } }, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index fe3e58ff9e..ed54e43c1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -17,6 +17,8 @@ import com.tangem.core.deeplink.global.ReferralDeepLink import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase +import com.tangem.domain.notifications.repository.NotificationsRepository +import com.tangem.domain.notifications.toggles.NotificationsFeatureToggles import com.tangem.domain.settings.* import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase @@ -25,6 +27,8 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.isMultiCurrency import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase +import com.tangem.domain.wallets.usecase.GetWalletsForAutomaticallyPushEnablingUseCase +import com.tangem.domain.wallets.usecase.SetNotificationsEnabledUseCase import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler import com.tangem.feature.wallet.presentation.router.InnerWalletRouter @@ -36,7 +40,6 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction @@ -45,6 +48,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.* import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider import com.tangem.features.biometry.AskBiometryComponent +import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull import com.tangem.utils.Provider @@ -86,11 +90,16 @@ internal class WalletModel @Inject constructor( private val appRouter: AppRouter, private val routingFeatureToggle: RoutingFeatureToggle, private val observeAndClearNFTCacheIfNeedUseCase: ObserveAndClearNFTCacheIfNeedUseCase, + private val notificationsRepository: NotificationsRepository, + private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase, + private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase, + private val notificationsFeatureToggles: NotificationsFeatureToggles, val screenLifecycleProvider: ScreenLifecycleProvider, val innerWalletRouter: InnerWalletRouter, ) : Model() { val askBiometryModelCallbacks = AskBiometryModelCallbacks() + val askForPushNotificationsModelCallbacks = AskForPushNotificationsCallbacks() val uiState: StateFlow = stateHolder.uiState private val walletsUpdateJobHolder = JobHolder() @@ -113,6 +122,7 @@ internal class WalletModel @Inject constructor( subscribeOnSelectedWalletFlow() subscribeToScreenBackgroundState() subscribeOnPushNotificationsPermission() + enableNotificationsIfNeeded() clickIntents.initialize(innerWalletRouter, modelScope) } @@ -196,20 +206,20 @@ internal class WalletModel @Inject constructor( private fun subscribeOnPushNotificationsPermission() { modelScope.launch { - val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) - val isPushPermissionAvailable = getPushPermissionOrNull() != null - if (!shouldRequestPush || !isPushPermissionAvailable) return@launch + val shouldAskPermission = shouldAskPermissionUseCase(PUSH_PERMISSION) + val afterUpdate = notificationsRepository.shouldShowSubscribeOnNotificationsAfterUpdate() + val shouldShowBottomSheet = if (notificationsFeatureToggles.isNotificationsEnabled) { + (shouldAskPermission || afterUpdate) && !shouldShowSaveWalletScreenUseCase() + } else { + val isPushPermissionAvailable = getPushPermissionOrNull() != null + shouldAskPermission && isPushPermissionAvailable + } + if (!shouldShowBottomSheet) return@launch delay(timeMillis = 1_800) - stateHolder.showBottomSheet( - content = PushNotificationsBottomSheetConfig( - onRequest = clickIntents::onRequestPushPermission, - onNeverRequest = { clickIntents.onNeverAskPushPermission(false) }, - onAllow = clickIntents::onAllowPushPermission, - onDeny = clickIntents::onDenyPushPermission, - ), - onDismiss = { clickIntents.onNeverAskPushPermission(true) }, + innerWalletRouter.dialogNavigation.activate( + configuration = WalletDialogConfig.AskForPushNotifications, ) } } @@ -433,6 +443,8 @@ internal class WalletModel @Inject constructor( it.copy(selectedWalletIndex = action.selectedWalletIndex) } } + + enableNotificationsIfNeeded() } private fun deleteWallet(action: WalletsUpdateActionResolver.Action.DeleteWallet) { @@ -526,6 +538,26 @@ internal class WalletModel @Inject constructor( } } + private fun enableNotificationsIfNeeded() { + if (!notificationsFeatureToggles.isNotificationsEnabled) return + modelScope.launch { + val isUserAllowToEnableNotifications = notificationsRepository.isUserAllowToSubscribeOnPushNotifications() + if (isUserAllowToEnableNotifications) { + val alreadyEnabledWallets = notificationsRepository.getWalletAutomaticallyEnabledList().map { + UserWalletId(it) + } + val walletsListWhichShouldBeEnabled = getWalletsListForEnablingUseCase(alreadyEnabledWallets) + walletsListWhichShouldBeEnabled.forEach { userWalletId -> + setNotificationsEnabledUseCase(userWalletId, true).onRight { + notificationsRepository.setNotificationsWasEnabledAutomatically(userWalletId.stringValue) + }.onLeft { + Timber.e(it) + } + } + } + } + } + inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks { override fun onAllowed() { analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On)) @@ -538,6 +570,23 @@ internal class WalletModel @Inject constructor( } } + inner class AskForPushNotificationsCallbacks : PushNotificationsModelCallbacks { + + override fun onAllowSystemPermission() { + innerWalletRouter.dialogNavigation.dismiss() + enableNotificationsIfNeeded() + } + + override fun onDenySystemPermission() { + innerWalletRouter.dialogNavigation.dismiss() + enableNotificationsIfNeeded() + } + + override fun onDismiss() { + innerWalletRouter.dialogNavigation.dismiss() + } + } + private companion object { const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L const val EXPRESS_STATUS_UPDATE_DELAY = 10000L diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt index 5ce5ac2dbd..77b86fba54 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt @@ -16,4 +16,7 @@ internal sealed interface WalletDialogConfig { @Serializable data object AskForBiometry : WalletDialogConfig + + @Serializable + data object AskForPushNotifications : WalletDialogConfig } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 7989bb0be0..1139c5b729 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -74,7 +74,6 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder -import com.tangem.feature.wallet.presentation.wallet.ui.components.PushNotificationsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* @@ -706,7 +705,6 @@ private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) { is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) - is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig) is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt deleted file mode 100644 index e61ceddcbf..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/PushNotificationsBottomSheet.kt +++ /dev/null @@ -1,111 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components - -import android.content.res.Configuration -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.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.components.SecondaryButton -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet -import com.tangem.core.ui.components.showcase.ShowcaseContent -import com.tangem.core.ui.components.showcase.model.ShowcaseItemModel -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.core.ui.utils.requestPushPermission -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig -import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull -import kotlinx.collections.immutable.persistentListOf - -@Composable -internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) { - TangemBottomSheet(config = config) { - PushNotificationsSheetContent(content = it, onDismiss = config.onDismissRequest) - } -} - -@Composable -private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) { - val requestPushPermission = requestPushPermission( - pushPermission = getPushPermissionOrNull(), - onAllow = { - content.onAllow() - onDismiss() - }, - onDeny = { - content.onDeny() - onDismiss() - }, - ) - - Column(modifier = Modifier.background(TangemTheme.colors.background.primary)) { - ShowcaseContent( - headerIconRes = R.drawable.ic_notifications_unread_24, - headerText = resourceReference(R.string.user_push_notification_agreement_header), - showcaseItems = persistentListOf( - ShowcaseItemModel( - iconRes = R.drawable.ic_rocket_launch_24, - text = resourceReference(R.string.user_push_notification_agreement_argument_one), - ), - ShowcaseItemModel( - iconRes = R.drawable.ic_storefront_24, - text = resourceReference(R.string.user_push_notification_agreement_argument_two), - ), - ), - modifier = Modifier.padding(top = TangemTheme.dimens.spacing40), - ) - Row( - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), - modifier = Modifier.padding( - start = TangemTheme.dimens.spacing16, - end = TangemTheme.dimens.spacing16, - top = TangemTheme.dimens.spacing40, - bottom = TangemTheme.dimens.spacing16, - ), - ) { - SecondaryButton( - text = stringResourceSafe(R.string.common_later), - onClick = { - content.onNeverRequest() - onDismiss() - }, - modifier = Modifier.weight(1f), - ) - PrimaryButton( - text = stringResourceSafe(R.string.common_allow), - onClick = { - content.onRequest() - requestPushPermission() - }, - modifier = Modifier.weight(1f), - ) - } - } -} - -// region Preview -@Preview(showBackground = true, widthDp = 360) -@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -private fun PushNotificationsSheetContent_Preview() { - TangemThemePreview { - PushNotificationsSheetContent( - PushNotificationsBottomSheetConfig( - onRequest = {}, - onNeverRequest = {}, - onAllow = {}, - onDeny = {}, - ), - onDismiss = {}, - ) - } -} -// endregion \ No newline at end of file