Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-25 13:21:07 +03:00
parent 74f71080ea
commit c70afd815e
15 changed files with 112 additions and 119 deletions

View file

@ -62,7 +62,6 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.features.wallet.navigation.WalletRouter
import com.tangem.google.GoogleServicesHelper import com.tangem.google.GoogleServicesHelper
@ -176,9 +175,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject @Inject
internal lateinit var routingComponentFactory: RoutingComponent.Factory internal lateinit var routingComponentFactory: RoutingComponent.Factory
@Inject
lateinit var pushNotificationsRouter: PushNotificationsRouter
@Inject @Inject
lateinit var cardRepository: CardRepository lateinit var cardRepository: CardRepository

View file

@ -33,7 +33,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
@ -67,7 +66,6 @@ data class DaggerGraphState(
val urlOpener: UrlOpener? = null, val urlOpener: UrlOpener? = null,
val shareManager: ShareManager? = null, val shareManager: ShareManager? = null,
val appRouter: AppRouter? = null, val appRouter: AppRouter? = null,
val pushNotificationsRouter: PushNotificationsRouter? = null,
val transactionSignerFactory: TransactionSignerFactory? = null, val transactionSignerFactory: TransactionSignerFactory? = null,
val getUserCountryUseCase: GetUserCountryUseCase? = null, val getUserCountryUseCase: GetUserCountryUseCase? = null,
val onrampFeatureToggles: OnrampFeatureToggles? = null, val onrampFeatureToggles: OnrampFeatureToggles? = null,

View file

@ -13,7 +13,7 @@ import com.tangem.features.managetokens.component.ManageTokensSource
import com.tangem.features.markets.details.MarketsTokenDetailsComponent import com.tangem.features.markets.details.MarketsTokenDetailsComponent
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
import com.tangem.features.onramp.component.* import com.tangem.features.onramp.component.*
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.pushnotifications.api.PushNotificationsComponent
import com.tangem.features.send.api.SendComponent import com.tangem.features.send.api.SendComponent
import com.tangem.features.swap.SwapComponent import com.tangem.features.swap.SwapComponent
import com.tangem.features.staking.api.StakingComponent import com.tangem.features.staking.api.StakingComponent
@ -72,9 +72,9 @@ internal class ChildFactory @Inject constructor(
private val securityModeComponentFactory: SecurityModeComponent.Factory, private val securityModeComponentFactory: SecurityModeComponent.Factory,
private val resetCardComponentFactory: ResetCardComponent.Factory, private val resetCardComponentFactory: ResetCardComponent.Factory,
private val referralComponentFactory: ReferralComponent.Factory, private val referralComponentFactory: ReferralComponent.Factory,
private val pushNotificationsComponentFactory: PushNotificationsComponent.Factory,
private val walletRouter: WalletRouter, private val walletRouter: WalletRouter,
private val testerRouter: TesterRouter, private val testerRouter: TesterRouter,
private val pushNotificationRouter: PushNotificationsRouter,
private val routingFeatureToggles: RoutingFeatureToggles, private val routingFeatureToggles: RoutingFeatureToggles,
) { ) {
@ -350,13 +350,19 @@ internal class ChildFactory @Inject constructor(
componentFactory = referralComponentFactory, componentFactory = referralComponentFactory,
) )
} }
is AppRoute.PushNotification -> {
createComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = pushNotificationsComponentFactory,
)
}
is AppRoute.SaveWallet, is AppRoute.SaveWallet,
is AppRoute.OnboardingNote, is AppRoute.OnboardingNote,
is AppRoute.OnboardingOther, is AppRoute.OnboardingOther,
is AppRoute.OnboardingTwins, is AppRoute.OnboardingTwins,
is AppRoute.OnboardingWallet, is AppRoute.OnboardingWallet,
is AppRoute.Wallet, is AppRoute.Wallet,
is AppRoute.PushNotification,
-> error("Unsupported route: $route") -> error("Unsupported route: $route")
} }
// endregion // endregion
@ -551,7 +557,11 @@ internal class ChildFactory @Inject constructor(
) )
} }
is AppRoute.PushNotification -> { is AppRoute.PushNotification -> {
route.asFragmentChild(Provider { pushNotificationRouter.entryFragment() }) route.asComponentChild(
contextProvider = contextProvider(route, contextFactory),
params = Unit,
componentFactory = pushNotificationsComponentFactory,
)
} }
is AppRoute.WalletSettings -> { is AppRoute.WalletSettings -> {
route.asComponentChild( route.asComponentChild(

View file

@ -2,10 +2,12 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.ui package com.tangem.features.onboarding.v2.multiwallet.impl.ui
import android.content.res.Configuration
import androidx.compose.animation.core.Transition import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -106,7 +108,7 @@ fun WalletArtworks(
} }
} }
val circleColor = TangemTheme.colors.background.secondary val circleColor = TangemTheme.colors.button.secondary
Canvas( Canvas(
modifier = Modifier modifier = Modifier
@ -451,11 +453,14 @@ private fun WalletArtworksState.toTransitionSetState(maxWidthDp: Float, maxHeigh
} }
@Preview(showBackground = true, widthDp = 360, heightDp = 640) @Preview(showBackground = true, widthDp = 360, heightDp = 640)
@Preview(showBackground = true, widthDp = 360, heightDp = 640, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun Preview() { private fun Preview() {
TangemThemePreview { TangemThemePreview {
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize(),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
var state: WalletArtworksState by remember { mutableStateOf(WalletArtworksState.Folded) } var state: WalletArtworksState by remember { mutableStateOf(WalletArtworksState.Folded) }

View file

@ -9,10 +9,11 @@ android {
} }
dependencies { dependencies {
/** AndroidX */ /** AndroidX */
implementation(deps.androidx.fragment.ktx) implementation(deps.androidx.fragment.ktx)
/** Core */ /** Core */
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.core.analytics.models) implementation(projects.core.analytics.models)
} }

View file

@ -0,0 +1,9 @@
package com.tangem.features.pushnotifications.api
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
interface PushNotificationsComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Unit, PushNotificationsComponent>
}

View file

@ -1,8 +0,0 @@
package com.tangem.features.pushnotifications.api.navigation
import androidx.fragment.app.Fragment
interface PushNotificationsRouter {
fun entryFragment(): Fragment
}

View file

@ -26,6 +26,7 @@ dependencies {
implementation(deps.kotlin.immutable.collections) implementation(deps.kotlin.immutable.collections)
/** Core modules */ /** Core modules */
implementation(projects.core.decompose)
implementation(projects.core.ui) implementation(projects.core.ui)
implementation(projects.core.configToggles) implementation(projects.core.configToggles)
implementation(projects.core.navigation) implementation(projects.core.navigation)

View file

@ -0,0 +1,42 @@
package com.tangem.features.pushnotifications.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import com.tangem.core.decompose.context.AppComponentContext
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.impl.model.PushNotificationsModel
import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsScreen
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultPushNotificationsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit,
) : PushNotificationsComponent, AppComponentContext by appComponentContext {
private val model: PushNotificationsModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
val activity = LocalContext.current.findActivity()
BackHandler(onBack = { activity.finish() })
NavigationBar3ButtonsScrim()
PushNotificationsScreen(
onRequest = model::onRequest,
onNeverRequest = model::onNeverRequest,
onAllowPermission = model::onAllowPermission,
onDenyPermission = model::onDenyPermission,
)
}
@AssistedFactory
interface Factory : PushNotificationsComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultPushNotificationsComponent
}
}

View file

@ -1,39 +0,0 @@
package com.tangem.features.pushnotifications.impl
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.features.pushnotifications.impl.presentation.ui.PushNotificationsScreen
import com.tangem.features.pushnotifications.impl.presentation.viewmodel.PushNotificationViewModel
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
internal class PushNotificationsFragment : ComposeFragment() {
@Inject
override lateinit var uiDependencies: UiDependencies
private val viewModel by viewModels<PushNotificationViewModel>()
@Composable
override fun ScreenContent(modifier: Modifier) {
BackHandler(onBack = requireActivity()::finish)
NavigationBar3ButtonsScrim()
PushNotificationsScreen(
onRequest = viewModel::onRequest,
onNeverRequest = viewModel::onNeverRequest,
onAllowPermission = viewModel::onAllowPermission,
onDenyPermission = viewModel::onDenyPermission,
)
}
companion object {
/** Create push notifications fragment instance */
fun create(): PushNotificationsFragment = PushNotificationsFragment()
}
}

View file

@ -1,24 +1,25 @@
package com.tangem.features.pushnotifications.impl.di package com.tangem.features.pushnotifications.impl.di
import com.tangem.common.routing.AppRouter import com.tangem.core.decompose.model.Model
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter import com.tangem.features.pushnotifications.api.PushNotificationsComponent
import com.tangem.features.pushnotifications.impl.navigation.DefaultPushNotificationsRouter import com.tangem.features.pushnotifications.impl.DefaultPushNotificationsComponent
import com.tangem.features.pushnotifications.impl.model.PushNotificationsModel
import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.scopes.ActivityScoped import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
/**
* DI module provides implementation of [PushNotificationsRouter]
*/
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(SingletonComponent::class)
object PushNotificationsModule { internal interface PushNotificationsModule {
@Provides @Binds
@ActivityScoped fun bindComponentFactory(impl: DefaultPushNotificationsComponent.Factory): PushNotificationsComponent.Factory
fun provideDisclaimerRouter(appRouter: AppRouter): PushNotificationsRouter {
return DefaultPushNotificationsRouter(appRouter) @Binds
} @IntoMap
@ClassKey(PushNotificationsModel::class)
fun bindModel(model: PushNotificationsModel): Model
} }

View file

@ -1,4 +1,4 @@
package com.tangem.features.pushnotifications.impl.presentation.viewmodel package com.tangem.features.pushnotifications.impl.model
internal interface PushNotificationsClickIntents { internal interface PushNotificationsClickIntents {
fun onRequest() fun onRequest()

View file

@ -1,26 +1,29 @@
package com.tangem.features.pushnotifications.impl.presentation.viewmodel package com.tangem.features.pushnotifications.impl.model
import androidx.lifecycle.ViewModel import androidx.compose.runtime.Stable
import androidx.lifecycle.viewModelScope import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.settings.NeverRequestPermissionUseCase import com.tangem.domain.settings.NeverRequestPermissionUseCase
import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase import com.tangem.domain.settings.NeverToInitiallyAskPermissionUseCase
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.pushnotifications.impl.navigation.DefaultPushNotificationsRouter import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@Suppress("LongParameterList") @Stable
@HiltViewModel @ComponentScoped
internal class PushNotificationViewModel @Inject constructor( class PushNotificationsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase, private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase, private val neverToInitiallyAskPermissionUseCase: NeverToInitiallyAskPermissionUseCase,
private val router: DefaultPushNotificationsRouter, private val appRouter: AppRouter,
private val analyticHandler: AnalyticsEventHandler, private val analyticHandler: AnalyticsEventHandler,
) : ViewModel(), PushNotificationsClickIntents { ) : Model(), PushNotificationsClickIntents {
override fun onRequest() { override fun onRequest() {
analyticHandler.send( analyticHandler.send(
@ -32,10 +35,10 @@ internal class PushNotificationViewModel @Inject constructor(
analyticHandler.send( analyticHandler.send(
PushNotificationAnalyticEvents.ButtonLater(AnalyticsParam.ScreensSources.Stories), PushNotificationAnalyticEvents.ButtonLater(AnalyticsParam.ScreensSources.Stories),
) )
viewModelScope.launch { modelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION) neverRequestPermissionUseCase(PUSH_PERMISSION)
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
router.openHome() appRouter.push(AppRoute.Home)
} }
} }
@ -43,10 +46,10 @@ internal class PushNotificationViewModel @Inject constructor(
analyticHandler.send( analyticHandler.send(
PushNotificationAnalyticEvents.PermissionStatus(isAllowed = true), PushNotificationAnalyticEvents.PermissionStatus(isAllowed = true),
) )
viewModelScope.launch { modelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION) neverRequestPermissionUseCase(PUSH_PERMISSION)
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
router.openHome() appRouter.push(AppRoute.Home)
} }
} }
@ -54,10 +57,10 @@ internal class PushNotificationViewModel @Inject constructor(
analyticHandler.send( analyticHandler.send(
PushNotificationAnalyticEvents.PermissionStatus(isAllowed = false), PushNotificationAnalyticEvents.PermissionStatus(isAllowed = false),
) )
viewModelScope.launch { modelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION) neverRequestPermissionUseCase(PUSH_PERMISSION)
neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION) neverToInitiallyAskPermissionUseCase(PUSH_PERMISSION)
router.openHome() appRouter.push(AppRoute.Home)
} }
} }
} }

View file

@ -1,18 +0,0 @@
package com.tangem.features.pushnotifications.impl.navigation
import androidx.fragment.app.Fragment
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.features.pushnotifications.impl.PushNotificationsFragment
import javax.inject.Inject
internal class DefaultPushNotificationsRouter @Inject constructor(
private val appRouter: AppRouter,
) : InnerPushNotificationsRouter {
override fun entryFragment(): Fragment = PushNotificationsFragment.create()
override fun openHome() {
appRouter.push(AppRoute.Home)
}
}

View file

@ -1,8 +0,0 @@
package com.tangem.features.pushnotifications.impl.navigation
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
interface InnerPushNotificationsRouter : PushNotificationsRouter {
fun openHome()
}