Updated on 2026-08-14
This commit is contained in:
parent
1a23bd8576
commit
796267c863
23 changed files with 265 additions and 53 deletions
|
|
@ -8,4 +8,5 @@ package com.tangem.common.ui.notifications
|
|||
*/
|
||||
enum class NotificationId(val key: String) {
|
||||
SendViaSwapTokenSelectorNotification("SendViaSwapTokenSelectorNotificationKey"),
|
||||
EnablePushesReminderNotification("EnablePushesReminderNotification"),
|
||||
}
|
||||
|
|
@ -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), {}),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
BIN
core/ui/src/main/res/drawable-hdpi/img_push_reminder.webp
Normal file
BIN
core/ui/src/main/res/drawable-hdpi/img_push_reminder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
core/ui/src/main/res/drawable-mdpi/img_push_reminder.webp
Normal file
BIN
core/ui/src/main/res/drawable-mdpi/img_push_reminder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
core/ui/src/main/res/drawable-xhdpi/img_push_reminder.webp
Normal file
BIN
core/ui/src/main/res/drawable-xhdpi/img_push_reminder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4 KiB |
BIN
core/ui/src/main/res/drawable-xxhdpi/img_push_reminder.webp
Normal file
BIN
core/ui/src/main/res/drawable-xxhdpi/img_push_reminder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
core/ui/src/main/res/drawable-xxxhdpi/img_push_reminder.webp
Normal file
BIN
core/ui/src/main/res/drawable-xxxhdpi/img_push_reminder.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
17
core/ui/src/main/res/drawable/ic_notification_56.xml
Normal file
17
core/ui/src/main/res/drawable/ic_notification_56.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M10.289,26.72C10.784,17.3 18.567,9.917 28,9.917C37.433,9.917 45.216,17.3 45.711,26.72C45.733,27.123 45.748,27.508 45.764,27.879L45.764,27.88C45.815,29.143 45.86,30.243 46.089,31.298C46.363,32.557 46.869,33.548 47.898,34.32C49.694,35.666 50.75,37.779 50.75,40.023C50.75,43.246 48.213,46.083 44.8,46.083H11.2C7.787,46.083 5.25,43.246 5.25,40.023C5.25,37.779 6.306,35.666 8.102,34.32C9.131,33.548 9.637,32.557 9.911,31.298C10.14,30.243 10.185,29.143 10.236,27.88L10.236,27.88C10.252,27.508 10.267,27.123 10.289,26.72Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M24.567,3.207C25.605,2.527 26.873,2.333 28,2.333C29.127,2.333 30.395,2.527 31.433,3.207C32.586,3.961 33.25,5.182 33.25,6.708C33.25,8.099 32.706,9.552 31.879,10.665C31.065,11.76 29.73,12.833 28,12.833C26.27,12.833 24.935,11.76 24.121,10.665C23.294,9.552 22.75,8.099 22.75,6.708C22.75,5.182 23.414,3.961 24.567,3.207Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M21,42C22.288,42 23.333,43.045 23.333,44.333C23.333,46.911 25.423,49 28,49C30.577,49 32.667,46.911 32.667,44.333C32.667,43.045 33.711,42 35,42C36.289,42 37.333,43.045 37.333,44.333C37.333,49.488 33.154,53.667 28,53.667C22.845,53.667 18.667,49.488 18.667,44.333C18.667,43.045 19.711,42 21,42Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
12
core/ui/src/main/res/drawable/ic_notification_square_24.xml
Normal file
12
core/ui/src/main/res/drawable/ic_notification_square_24.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M14.75,5.75C14.75,3.541 16.541,1.75 18.75,1.75C20.959,1.75 22.75,3.541 22.75,5.75C22.75,7.959 20.959,9.75 18.75,9.75C16.541,9.75 14.75,7.959 14.75,5.75Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M12.663,2.75C13.279,2.75 13.587,2.75 13.707,2.914C13.827,3.078 13.72,3.412 13.508,4.08C13.34,4.607 13.25,5.168 13.25,5.75C13.25,8.788 15.712,11.25 18.75,11.25C19.339,11.25 19.907,11.157 20.44,10.985C21.091,10.775 21.418,10.671 21.582,10.79C21.746,10.91 21.747,11.209 21.749,11.809C21.75,12.172 21.75,12.55 21.75,12.942V13.058C21.75,15.248 21.75,16.968 21.569,18.312C21.384,19.688 20.997,20.781 20.139,21.639C19.281,22.497 18.188,22.884 16.812,23.069C15.468,23.25 13.748,23.25 11.558,23.25H11.442C9.252,23.25 7.532,23.25 6.188,23.069C4.812,22.884 3.72,22.497 2.861,21.639C2.003,20.781 1.616,19.688 1.431,18.312C1.25,16.968 1.25,15.248 1.25,13.058V12.942C1.25,10.752 1.25,9.032 1.431,7.688C1.616,6.312 2.003,5.22 2.861,4.361C3.72,3.503 4.812,3.116 6.188,2.931C7.532,2.75 9.252,2.75 11.442,2.75H12.663ZM7,15.75C6.586,15.75 6.25,16.086 6.25,16.5C6.25,16.914 6.586,17.25 7,17.25H15C15.414,17.25 15.75,16.914 15.75,16.5C15.75,16.086 15.414,15.75 15,15.75H7ZM7,10.75C6.586,10.75 6.25,11.086 6.25,11.5C6.25,11.914 6.586,12.25 7,12.25H11C11.414,12.25 11.75,11.914 11.75,11.5C11.75,11.086 11.414,10.75 11,10.75H7Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
15
core/ui/src/main/res/drawable/ic_stars_24.xml
Normal file
15
core/ui/src/main/res/drawable/ic_stars_24.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M12,2.75C12.414,2.75 12.75,3.086 12.75,3.5C12.75,5.506 13.761,7.575 15.343,9.157C16.925,10.739 18.994,11.75 21,11.75C21.414,11.75 21.75,12.086 21.75,12.5C21.75,12.914 21.414,13.25 21,13.25C18.994,13.25 16.925,14.261 15.343,15.843C13.761,17.425 12.75,19.494 12.75,21.5C12.75,21.914 12.414,22.25 12,22.25C11.586,22.25 11.25,21.914 11.25,21.5C11.25,19.494 10.239,17.425 8.657,15.843C7.075,14.261 5.006,13.25 3,13.25C2.586,13.25 2.25,12.914 2.25,12.5C2.25,12.086 2.586,11.75 3,11.75C5.006,11.75 7.075,10.739 8.657,9.157C10.239,7.575 11.25,5.506 11.25,3.5C11.25,3.086 11.586,2.75 12,2.75Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M19.25,1.75C19.463,1.75 19.648,1.898 19.695,2.106L19.93,3.146C20.09,3.856 20.644,4.41 21.354,4.57L22.394,4.805C22.602,4.852 22.75,5.037 22.75,5.25C22.75,5.463 22.602,5.648 22.394,5.695L21.354,5.93C20.644,6.09 20.09,6.644 19.93,7.354L19.695,8.394C19.648,8.602 19.463,8.75 19.25,8.75C19.037,8.75 18.852,8.602 18.805,8.394L18.57,7.354C18.41,6.644 17.856,6.09 17.146,5.93L16.106,5.695C15.898,5.648 15.75,5.463 15.75,5.25C15.75,5.037 15.898,4.852 16.106,4.805L17.146,4.57C17.856,4.41 18.41,3.856 18.57,3.146L18.805,2.106C18.852,1.898 19.037,1.75 19.25,1.75Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M4.75,16.25C4.963,16.25 5.148,16.398 5.195,16.606L5.43,17.646C5.59,18.356 6.144,18.91 6.854,19.07L7.894,19.305C8.102,19.352 8.25,19.537 8.25,19.75C8.25,19.963 8.102,20.148 7.894,20.195L6.854,20.43C6.144,20.59 5.59,21.144 5.43,21.854L5.195,22.894C5.148,23.102 4.963,23.25 4.75,23.25C4.537,23.25 4.352,23.102 4.305,22.894L4.07,21.854C3.91,21.144 3.356,20.59 2.646,20.43L1.606,20.195C1.398,20.148 1.25,19.963 1.25,19.75C1.25,19.537 1.398,19.352 1.606,19.305L2.646,19.07C3.356,18.91 3.91,18.356 4.07,17.646L4.305,16.606C4.352,16.398 4.537,16.25 4.75,16.25Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -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<Boolean> {
|
||||
return appPreferencesStore.get(PreferencesKeys.getShouldShowNotificationKey(key), true)
|
||||
}
|
||||
|
||||
override suspend fun setShouldShowNotifications(key: String, value: Boolean) {
|
||||
appPreferencesStore.store(PreferencesKeys.getShouldShowNotificationKey(key), value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Boolean>
|
||||
|
||||
/**
|
||||
* Sets whether a notification with the given [key] should be shown to the user.
|
||||
* @param key The unique identifier for the notification.
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<CryptoCurrency>) {
|
||||
coroutineScope {
|
||||
listOf(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ImmutableList<WalletNotification>> {
|
||||
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<TokenListError, TokenList>
|
||||
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<WalletNotification>.addPushReminderNotification(
|
||||
clickIntents: WalletClickIntents,
|
||||
shouldShowPushReminderBanner: Boolean,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.PushNotifications(
|
||||
onCloseClick = clickIntents::onDenyPermissions,
|
||||
onEnabledClick = clickIntents::onAllowPermissions,
|
||||
),
|
||||
condition = shouldShowPushReminderBanner,
|
||||
)
|
||||
}
|
||||
|
||||
private fun Lce<TokenListError, TokenList>.hasUnreachableNetworks(): Boolean {
|
||||
val tokenList = getOrNull(isPartialContentAccepted = false) ?: return false
|
||||
|
||||
|
|
|
|||
|
|
@ -30,4 +30,9 @@ internal sealed class WalletEvent {
|
|||
RIGHT,
|
||||
}
|
||||
}
|
||||
|
||||
data class RequestPushPermissions(
|
||||
val onAllow: () -> Unit,
|
||||
val onDeny: () -> Unit,
|
||||
) : WalletEvent()
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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<Pair<() -> 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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue