Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-19 21:11:19 +05:00
parent 5566331bdb
commit 76c5fd4032
16 changed files with 338 additions and 92 deletions

View file

@ -0,0 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
data class PushNotificationsBottomSheetConfig(
val isFirstTimeAsking: Boolean,
val onAllow: () -> Unit,
val onAllowed: () -> Unit,
val onDeny: () -> Unit,
val openSettings: () -> Unit,
) : TangemBottomSheetConfigContent

View file

@ -37,6 +37,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
@ -53,6 +54,7 @@ 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.*
@ -204,17 +206,7 @@ private fun WalletContent(
organizeTokens(state = selectedWallet, itemModifier = itemModifier)
}
val bottomSheetConfig = selectedWallet.bottomSheetConfig
if (bottomSheetConfig != null) {
when (bottomSheetConfig.content) {
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig)
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
}
}
ShowBottomSheet(bottomSheetConfig = selectedWallet.bottomSheetConfig)
WalletsListEffects(
lazyListState = walletsListState,
@ -606,6 +598,21 @@ internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modi
}
}
@Composable
private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
if (bottomSheetConfig != null) {
when (bottomSheetConfig.content) {
is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig)
is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig)
is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig)
is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig)
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig)
}
}
}
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)

View file

@ -0,0 +1,117 @@
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.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
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.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.PushNotificationsBottomSheetConfig
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun PushNotificationsBottomSheet(config: TangemBottomSheetConfig) {
TangemBottomSheet<PushNotificationsBottomSheetConfig>(config = config) {
PushNotificationsSheetContent(content = it, onDismiss = config.onDismissRequest)
}
}
@Composable
private fun PushNotificationsSheetContent(content: PushNotificationsBottomSheetConfig, onDismiss: () -> Unit) {
val isClicked = remember { mutableStateOf(false) }
val requestPushPermission = requestPushPermission(
isFirstTimeAsking = content.isFirstTimeAsking,
isClicked = isClicked,
onAllow = {
content.onAllowed()
onDismiss()
},
onDeny = {
content.onDeny()
onDismiss()
},
onOpenSettings = content.openSettings,
)
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 = stringResource(R.string.common_later),
onClick = {
content.onDeny()
onDismiss()
},
modifier = Modifier.weight(1f),
)
PrimaryButton(
text = stringResource(R.string.common_allow),
onClick = {
isClicked.value = true
content.onAllow()
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(
isFirstTimeAsking = false,
onAllow = {},
onAllowed = {},
onDeny = {},
openSettings = {},
),
onDismiss = {},
)
}
}
// endregion

View file

@ -4,11 +4,11 @@ import androidx.compose.runtime.MutableState
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled
import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
import com.tangem.domain.settings.*
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
@ -19,6 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWal
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.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletPullToRefreshConfig
@ -28,6 +29,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSend
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.managetokens.navigation.ExpandableState
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
@ -60,6 +62,10 @@ internal class WalletViewModel @Inject constructor(
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
private val walletDeepLinksHandler: WalletDeepLinksHandler,
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
private val shouldInitiallyAskPermissionUseCase: ShouldInitiallyAskPermissionUseCase,
private val isFirstTimeAskingPermissionUseCase: IsFirstTimeAskingPermissionUseCase,
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
private val settingsManager: SettingsManager,
analyticsEventsHandler: AnalyticsEventHandler,
) : ViewModel() {
@ -80,6 +86,7 @@ internal class WalletViewModel @Inject constructor(
subscribeOnBalanceHiding()
subscribeOnSelectedWalletFlow()
subscribeToScreenBackgroundState()
subscribeOnPushNotificationsPermission()
}
private fun maybeMigrateNames() {
@ -145,6 +152,30 @@ internal class WalletViewModel @Inject constructor(
.launchIn(viewModelScope)
}
private fun subscribeOnPushNotificationsPermission() {
viewModelScope.launch {
if (!shouldAskPermissionUseCase(PUSH_PERMISSION)) return@launch
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
val isFirstTimeAsking = isFirstTimeAskingPermissionUseCase(PUSH_PERMISSION).getOrElse { true }
val wasInitiallyAsk = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { true }
val onDenyClick: () -> Unit = if (wasInitiallyAsk) {
clickIntents::onDelayAskPushPermission
} else {
clickIntents::onNeverAskPushPermission
}
stateHolder.showBottomSheet(
PushNotificationsBottomSheetConfig(
isFirstTimeAsking = isFirstTimeAsking,
onAllow = clickIntents::onAllowPushPermission,
onAllowed = clickIntents::onNeverAskPushPermission,
onDeny = onDenyClick,
openSettings = settingsManager::openSettings,
),
)
}
}
private fun subscribeOnSelectedWalletFlow() {
getSelectedWalletUseCase().onRight {
it

View file

@ -30,6 +30,7 @@ internal class WalletClickIntents @Inject constructor(
private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor,
private val contentClickIntentsImplementor: WalletContentClickIntentsImplementor,
private val visaWalletIntentsImplementor: VisaWalletIntentsImplementor,
private val pushPermissionClickIntentsImplementor: WalletPushPermissionClickIntentsImplementor,
private val stateHolder: WalletStateController,
private val walletScreenContentLoader: WalletScreenContentLoader,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
@ -45,7 +46,8 @@ internal class WalletClickIntents @Inject constructor(
WalletWarningsClickIntents by warningsClickIntentsImplementer,
WalletCurrencyActionsClickIntents by currencyActionsClickIntentsImplementor,
WalletContentClickIntents by contentClickIntentsImplementor,
VisaWalletIntents by visaWalletIntentsImplementor {
VisaWalletIntents by visaWalletIntentsImplementor,
WalletPushPermissionClickIntents by pushPermissionClickIntentsImplementor {
override fun initialize(router: InnerWalletRouter, coroutineScope: CoroutineScope) {
super.initialize(router, coroutineScope)
@ -55,6 +57,7 @@ internal class WalletClickIntents @Inject constructor(
currencyActionsClickIntentsImplementor.initialize(router, coroutineScope)
contentClickIntentsImplementor.initialize(router, coroutineScope)
visaWalletIntentsImplementor.initialize(router, coroutineScope)
pushPermissionClickIntentsImplementor.initialize(router, coroutineScope)
}
fun onWalletChange(index: Int) {

View file

@ -0,0 +1,44 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import com.tangem.domain.settings.DelayPermissionRequestUseCase
import com.tangem.domain.settings.NeverRequestPermissionUseCase
import com.tangem.domain.settings.SetFirstTimeAskingPermissionUseCase
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.launch
import javax.inject.Inject
internal interface WalletPushPermissionClickIntents {
fun onAllowPushPermission()
fun onDelayAskPushPermission()
fun onNeverAskPushPermission()
}
@ViewModelScoped
internal class WalletPushPermissionClickIntentsImplementor @Inject constructor(
private val setFirstTimeAskingPermissionUseCase: SetFirstTimeAskingPermissionUseCase,
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
private val delayPermissionRequestUseCase: DelayPermissionRequestUseCase,
) : BaseWalletClickIntents(), WalletPushPermissionClickIntents {
override fun onAllowPushPermission() {
viewModelScope.launch {
setFirstTimeAskingPermissionUseCase(PUSH_PERMISSION)
}
}
override fun onDelayAskPushPermission() {
viewModelScope.launch {
delayPermissionRequestUseCase(PUSH_PERMISSION)
}
}
override fun onNeverAskPushPermission() {
viewModelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION)
}
}
}