Updated on 2026-08-14
This commit is contained in:
parent
73b80b5ef9
commit
dad8ccb8da
27 changed files with 24 additions and 677 deletions
|
|
@ -32,7 +32,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
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.details.DetailsFeatureToggles
|
import com.tangem.features.details.DetailsFeatureToggles
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
||||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||||
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles
|
||||||
|
|
@ -118,8 +117,6 @@ interface ApplicationEntryPoint {
|
||||||
|
|
||||||
fun getAppRouter(): AppRouter
|
fun getAppRouter(): AppRouter
|
||||||
|
|
||||||
fun getPushNotificationsFeatureToggles(): PushNotificationsFeatureToggles
|
|
||||||
|
|
||||||
fun getTangemAppLogger(): TangemAppLoggerInitializer
|
fun getTangemAppLogger(): TangemAppLoggerInitializer
|
||||||
|
|
||||||
fun getHomeFeatureToggles(): HomeFeatureToggles
|
fun getHomeFeatureToggles(): HomeFeatureToggles
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ import com.tangem.tap.common.OnActivityResultCallback
|
||||||
import com.tangem.tap.common.SnackbarHandler
|
import com.tangem.tap.common.SnackbarHandler
|
||||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||||
import com.tangem.tap.common.extensions.inject
|
|
||||||
import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss
|
import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss
|
||||||
import com.tangem.tap.common.redux.NotificationsHandler
|
import com.tangem.tap.common.redux.NotificationsHandler
|
||||||
import com.tangem.tap.domain.sdk.TangemSdkManager
|
import com.tangem.tap.domain.sdk.TangemSdkManager
|
||||||
|
|
@ -78,7 +77,6 @@ import com.tangem.tap.features.main.model.Toast
|
||||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||||
import com.tangem.tap.proxy.AppStateHolder
|
import com.tangem.tap.proxy.AppStateHolder
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
|
||||||
import com.tangem.tap.routing.RoutingComponent
|
import com.tangem.tap.routing.RoutingComponent
|
||||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||||
|
|
@ -539,11 +537,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val toggles = store.inject(getDependency = DaggerGraphState::pushNotificationsFeatureToggles)
|
val shouldShowTos = !cardRepository.isTangemTOSAccepted()
|
||||||
val isPushPermissionEnabled = toggles.isPushNotificationsEnabled
|
val shouldShowInitialPush = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { false }
|
||||||
val shouldShowTos = !cardRepository.isTangemTOSAccepted() && isPushPermissionEnabled
|
|
||||||
val wasPushInitiallyAsked = shouldInitiallyAskPermissionUseCase(PUSH_PERMISSION).getOrElse { false }
|
|
||||||
val shouldShowInitialPush = wasPushInitiallyAsked && isPushPermissionEnabled
|
|
||||||
|
|
||||||
val route = when {
|
val route = when {
|
||||||
shouldShowTos -> AppRoute.Disclaimer(isTosAccepted = false)
|
shouldShowTos -> AppRoute.Disclaimer(isTosAccepted = false)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
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.details.DetailsFeatureToggles
|
import com.tangem.features.details.DetailsFeatureToggles
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.tap.common.analytics.AnalyticsFactory
|
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||||
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||||
|
|
@ -188,9 +187,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
private val appRouter: AppRouter
|
private val appRouter: AppRouter
|
||||||
get() = entryPoint.getAppRouter()
|
get() = entryPoint.getAppRouter()
|
||||||
|
|
||||||
private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles
|
|
||||||
get() = entryPoint.getPushNotificationsFeatureToggles()
|
|
||||||
|
|
||||||
private val tangemAppLoggerInitializer: TangemAppLoggerInitializer
|
private val tangemAppLoggerInitializer: TangemAppLoggerInitializer
|
||||||
get() = entryPoint.getTangemAppLogger()
|
get() = entryPoint.getTangemAppLogger()
|
||||||
|
|
||||||
|
|
@ -280,7 +276,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
|
||||||
urlOpener = urlOpener,
|
urlOpener = urlOpener,
|
||||||
shareManager = shareManager,
|
shareManager = shareManager,
|
||||||
appRouter = appRouter,
|
appRouter = appRouter,
|
||||||
pushNotificationsFeatureToggles = pushNotificationsFeatureToggles,
|
|
||||||
homeFeatureToggles = homeFeatureToggles,
|
homeFeatureToggles = homeFeatureToggles,
|
||||||
getUserCountryUseCase = getUserCountryUseCase,
|
getUserCountryUseCase = getUserCountryUseCase,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap.common.redux
|
||||||
import com.tangem.tap.common.redux.global.globalReducer
|
import com.tangem.tap.common.redux.global.globalReducer
|
||||||
import com.tangem.tap.features.details.redux.DetailsReducer
|
import com.tangem.tap.features.details.redux.DetailsReducer
|
||||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
|
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer
|
|
||||||
import com.tangem.tap.features.home.redux.HomeReducer
|
import com.tangem.tap.features.home.redux.HomeReducer
|
||||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
|
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
|
||||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
|
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
|
||||||
|
|
@ -28,7 +27,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
||||||
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
|
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
|
||||||
twinCardsState = TwinCardsReducer.reduce(action, state),
|
twinCardsState = TwinCardsReducer.reduce(action, state),
|
||||||
detailsState = DetailsReducer.reduce(action, state),
|
detailsState = DetailsReducer.reduce(action, state),
|
||||||
disclaimerState = DisclaimerReducer.reduce(action, state),
|
|
||||||
tokensState = TokensReducer.reduce(action, state),
|
tokensState = TokensReducer.reduce(action, state),
|
||||||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
|
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
|
||||||
welcomeState = WelcomeReducer.reduce(action, state),
|
welcomeState = WelcomeReducer.reduce(action, state),
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ import com.tangem.tap.features.details.redux.DetailsMiddleware
|
||||||
import com.tangem.tap.features.details.redux.DetailsState
|
import com.tangem.tap.features.details.redux.DetailsState
|
||||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware
|
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddleware
|
||||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
|
|
||||||
import com.tangem.tap.features.home.redux.HomeMiddleware
|
import com.tangem.tap.features.home.redux.HomeMiddleware
|
||||||
import com.tangem.tap.features.home.redux.HomeState
|
import com.tangem.tap.features.home.redux.HomeState
|
||||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware
|
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware
|
||||||
|
|
@ -43,7 +41,6 @@ data class AppState(
|
||||||
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
|
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
|
||||||
val twinCardsState: TwinCardsState = TwinCardsState(),
|
val twinCardsState: TwinCardsState = TwinCardsState(),
|
||||||
val detailsState: DetailsState = DetailsState(),
|
val detailsState: DetailsState = DetailsState(),
|
||||||
val disclaimerState: DisclaimerState = DisclaimerState(),
|
|
||||||
val tokensState: TokensState = TokensState(),
|
val tokensState: TokensState = TokensState(),
|
||||||
val walletConnectState: WalletConnectState = WalletConnectState(),
|
val walletConnectState: WalletConnectState = WalletConnectState(),
|
||||||
val welcomeState: WelcomeState = WelcomeState(),
|
val welcomeState: WelcomeState = WelcomeState(),
|
||||||
|
|
@ -75,7 +72,6 @@ data class AppState(
|
||||||
OnboardingOtherCardsMiddleware.handler,
|
OnboardingOtherCardsMiddleware.handler,
|
||||||
TwinCardsMiddleware.handler,
|
TwinCardsMiddleware.handler,
|
||||||
DetailsMiddleware().detailsMiddleware,
|
DetailsMiddleware().detailsMiddleware,
|
||||||
DisclaimerMiddleware().disclaimerMiddleware,
|
|
||||||
WalletConnectMiddleware().walletConnectMiddleware,
|
WalletConnectMiddleware().walletConnectMiddleware,
|
||||||
BackupMiddleware().backupMiddleware,
|
BackupMiddleware().backupMiddleware,
|
||||||
WelcomeMiddleware().middleware,
|
WelcomeMiddleware().middleware,
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ import com.tangem.operations.attestation.Attestation
|
||||||
import com.tangem.tap.common.extensions.inject
|
import com.tangem.tap.common.extensions.inject
|
||||||
import com.tangem.tap.common.extensions.setContext
|
import com.tangem.tap.common.extensions.setContext
|
||||||
import com.tangem.tap.common.redux.global.GlobalAction
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
|
||||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||||
import com.tangem.tap.store
|
import com.tangem.tap.store
|
||||||
|
|
@ -62,7 +60,6 @@ class TapWalletManager(
|
||||||
updateConfigManager(scanResponse)
|
updateConfigManager(scanResponse)
|
||||||
withMainContext {
|
withMainContext {
|
||||||
// Order is important
|
// Order is important
|
||||||
store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer()))
|
|
||||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||||
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
|
||||||
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
|
store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.domain.scanCard
|
||||||
|
|
||||||
import com.tangem.common.CompletionResult
|
import com.tangem.common.CompletionResult
|
||||||
import com.tangem.common.core.TangemError
|
import com.tangem.common.core.TangemError
|
||||||
import com.tangem.common.core.TangemSdkError
|
|
||||||
import com.tangem.common.doOnFailure
|
import com.tangem.common.doOnFailure
|
||||||
import com.tangem.common.doOnSuccess
|
import com.tangem.common.doOnSuccess
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
|
|
@ -18,9 +17,6 @@ import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
|
||||||
import com.tangem.tap.common.extensions.*
|
import com.tangem.tap.common.extensions.*
|
||||||
import com.tangem.tap.common.redux.global.GlobalAction
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerSource
|
|
||||||
import com.tangem.tap.features.onboarding.OnboardingHelper
|
import com.tangem.tap.features.onboarding.OnboardingHelper
|
||||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
|
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
|
||||||
|
|
@ -102,6 +98,8 @@ internal object LegacyScanProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: [REDACTED_JIRA]
|
||||||
|
@Suppress("UnusedPrivateMember")
|
||||||
private suspend inline fun showDisclaimerIfNeed(
|
private suspend inline fun showDisclaimerIfNeed(
|
||||||
scanResponse: ScanResponse,
|
scanResponse: ScanResponse,
|
||||||
crossinline disclaimerWillShow: () -> Unit = {},
|
crossinline disclaimerWillShow: () -> Unit = {},
|
||||||
|
|
@ -109,7 +107,6 @@ internal object LegacyScanProcessor {
|
||||||
crossinline onFailure: suspend (error: TangemError) -> Unit,
|
crossinline onFailure: suspend (error: TangemError) -> Unit,
|
||||||
) {
|
) {
|
||||||
val disclaimer = scanResponse.card.createDisclaimer()
|
val disclaimer = scanResponse.card.createDisclaimer()
|
||||||
store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer))
|
|
||||||
|
|
||||||
if (disclaimer.isAccepted()) {
|
if (disclaimer.isAccepted()) {
|
||||||
nextHandler(scanResponse)
|
nextHandler(scanResponse)
|
||||||
|
|
@ -120,23 +117,9 @@ internal object LegacyScanProcessor {
|
||||||
withContext(Dispatchers.Main.immediate) {
|
withContext(Dispatchers.Main.immediate) {
|
||||||
disclaimerWillShow()
|
disclaimerWillShow()
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatchNavigationAction {
|
||||||
DisclaimerAction.Show(
|
push(AppRoute.Disclaimer(isTosAccepted = false))
|
||||||
from = DisclaimerSource.Home,
|
}
|
||||||
callback = DisclaimerCallback(
|
|
||||||
onAccept = {
|
|
||||||
scope.launch(Dispatchers.Main.immediate) {
|
|
||||||
nextHandler(scanResponse)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismiss = {
|
|
||||||
scope.launch(Dispatchers.Main.immediate) {
|
|
||||||
onFailure(TangemSdkError.UserCancelled())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,7 @@ internal object UseCaseScanProcessor {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
add(AnalyticsChain(Basic.CardWasScanned(analyticsSource)))
|
add(AnalyticsChain(Basic.CardWasScanned(analyticsSource)))
|
||||||
val pushNotificationsToggles =
|
add(DisclaimerChain(store, disclaimerWillShow))
|
||||||
store.inject(getDependency = DaggerGraphState::pushNotificationsFeatureToggles)
|
|
||||||
if (pushNotificationsToggles.isPushNotificationsEnabled) {
|
|
||||||
add(DisclaimerChain(store, disclaimerWillShow))
|
|
||||||
}
|
|
||||||
add(CheckForOnboardingChain(store, store.state.globalState.tapWalletManager))
|
add(CheckForOnboardingChain(store, store.state.globalState.tapWalletManager))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
package com.tangem.tap.domain.scanCard.chains
|
package com.tangem.tap.domain.scanCard.chains
|
||||||
|
|
||||||
import arrow.core.left
|
|
||||||
import arrow.core.right
|
import arrow.core.right
|
||||||
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.domain.card.ScanCardException
|
import com.tangem.domain.card.ScanCardException
|
||||||
import com.tangem.domain.core.chain.Chain
|
import com.tangem.domain.core.chain.Chain
|
||||||
import com.tangem.domain.core.chain.ResultChain
|
import com.tangem.domain.core.chain.ResultChain
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||||
import com.tangem.tap.common.redux.AppState
|
import com.tangem.tap.common.redux.AppState
|
||||||
import com.tangem.tap.features.disclaimer.Disclaimer
|
|
||||||
import com.tangem.tap.features.disclaimer.createDisclaimer
|
import com.tangem.tap.features.disclaimer.createDisclaimer
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerSource
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import org.rekotlin.Store
|
import org.rekotlin.Store
|
||||||
import kotlin.coroutines.resume
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles disclaimer display after the card scanning operation.
|
* Handles disclaimer display after the card scanning operation.
|
||||||
|
|
@ -40,31 +34,13 @@ internal class DisclaimerChain(
|
||||||
previousChainResult.right()
|
previousChainResult.right()
|
||||||
} else {
|
} else {
|
||||||
disclaimerWillShow()
|
disclaimerWillShow()
|
||||||
showDisclaimer(disclaimer, previousChainResult)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun showDisclaimer(disclaimer: Disclaimer, response: ScanResponse): ScanChainResult {
|
// TODO: [REDACTED_JIRA]
|
||||||
store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer))
|
store.dispatchNavigationAction {
|
||||||
|
push(route = AppRoute.Disclaimer(isTosAccepted = false))
|
||||||
|
}
|
||||||
|
|
||||||
return suspendCancellableCoroutine { continuation ->
|
previousChainResult.right()
|
||||||
store.dispatchOnMain(
|
|
||||||
DisclaimerAction.Show(
|
|
||||||
from = DisclaimerSource.Home,
|
|
||||||
callback = DisclaimerCallback(
|
|
||||||
onAccept = {
|
|
||||||
if (continuation.isActive) {
|
|
||||||
continuation.resume(response.right())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDismiss = {
|
|
||||||
if (continuation.isActive) {
|
|
||||||
continuation.resume(ScanChainException.DisclaimerWasCanceled.left())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.core.analytics.Analytics
|
import com.tangem.core.analytics.Analytics
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam
|
import com.tangem.core.analytics.models.AnalyticsParam
|
||||||
import com.tangem.core.analytics.models.Basic
|
import com.tangem.core.analytics.models.Basic
|
||||||
|
|
||||||
import com.tangem.core.ui.event.StateEvent
|
import com.tangem.core.ui.event.StateEvent
|
||||||
import com.tangem.core.ui.event.consumedEvent
|
import com.tangem.core.ui.event.consumedEvent
|
||||||
import com.tangem.core.ui.event.triggeredEvent
|
import com.tangem.core.ui.event.triggeredEvent
|
||||||
|
|
@ -22,8 +21,6 @@ import com.tangem.tap.common.redux.AppState
|
||||||
import com.tangem.tap.common.redux.global.GlobalAction
|
import com.tangem.tap.common.redux.global.GlobalAction
|
||||||
import com.tangem.tap.features.details.redux.DetailsAction
|
import com.tangem.tap.features.details.redux.DetailsAction
|
||||||
import com.tangem.tap.features.details.redux.DetailsState
|
import com.tangem.tap.features.details.redux.DetailsState
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerSource
|
|
||||||
import com.tangem.tap.features.home.LocaleRegionProvider
|
import com.tangem.tap.features.home.LocaleRegionProvider
|
||||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||||
import com.tangem.tap.scope
|
import com.tangem.tap.scope
|
||||||
|
|
@ -128,7 +125,9 @@ internal class DetailsViewModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToToS() {
|
private fun navigateToToS() {
|
||||||
store.dispatchOnMain(DisclaimerAction.Show(DisclaimerSource.Details))
|
store.dispatchNavigationAction {
|
||||||
|
push(AppRoute.Disclaimer(isTosAccepted = true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToReferralProgram() {
|
private fun navigateToReferralProgram() {
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,6 @@ abstract class BaseDisclaimer(
|
||||||
override suspend fun isAccepted(): Boolean = dataProvider.isAccepted()
|
override suspend fun isAccepted(): Boolean = dataProvider.isAccepted()
|
||||||
}
|
}
|
||||||
|
|
||||||
class DummyDisclaimer : Disclaimer {
|
|
||||||
override fun getUri(): Uri = Uri.parse("https://tangem.com/tangem_tos.html")
|
|
||||||
override suspend fun accept() {}
|
|
||||||
override suspend fun isAccepted(): Boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class TangemDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
|
class TangemDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
|
||||||
override fun getUri(): Uri = Uri.parse("$baseUrl/tangem_tos.html")
|
override fun getUri(): Uri = Uri.parse("$baseUrl/tangem_tos.html")
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.redux
|
|
||||||
|
|
||||||
import com.tangem.tap.common.entities.ProgressState
|
|
||||||
import com.tangem.tap.features.disclaimer.Disclaimer
|
|
||||||
import org.rekotlin.Action
|
|
||||||
|
|
||||||
sealed class DisclaimerAction : Action {
|
|
||||||
|
|
||||||
data class SetDisclaimer(val disclaimer: Disclaimer) : DisclaimerAction()
|
|
||||||
|
|
||||||
data class Show(
|
|
||||||
val from: DisclaimerSource,
|
|
||||||
val callback: DisclaimerCallback? = null,
|
|
||||||
) : DisclaimerAction()
|
|
||||||
|
|
||||||
object AcceptDisclaimer : DisclaimerAction()
|
|
||||||
object OnBackPressed : DisclaimerAction()
|
|
||||||
|
|
||||||
data class OnProgressStateChanged(val state: ProgressState?) : DisclaimerAction()
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.redux
|
|
||||||
|
|
||||||
import com.tangem.common.routing.AppRoute
|
|
||||||
import com.tangem.common.routing.AppRouter
|
|
||||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
|
||||||
import com.tangem.tap.common.redux.AppState
|
|
||||||
import com.tangem.tap.mainScope
|
|
||||||
import com.tangem.tap.store
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.rekotlin.Action
|
|
||||||
import org.rekotlin.Middleware
|
|
||||||
|
|
||||||
class DisclaimerMiddleware {
|
|
||||||
val disclaimerMiddleware: Middleware<AppState> = { dispatch, state ->
|
|
||||||
{ next ->
|
|
||||||
{ action ->
|
|
||||||
state()?.let { handleDisclaimerMiddleware(action, it) }
|
|
||||||
next(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleDisclaimerMiddleware(action: Action, appState: AppState) {
|
|
||||||
val state = appState.disclaimerState
|
|
||||||
|
|
||||||
when (action) {
|
|
||||||
is DisclaimerAction.Show -> {
|
|
||||||
store.dispatchNavigationAction {
|
|
||||||
push(AppRoute.Disclaimer(isTosAccepted = action.from == DisclaimerSource.Details))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is DisclaimerAction.AcceptDisclaimer -> {
|
|
||||||
mainScope.launch {
|
|
||||||
state.disclaimer.accept()
|
|
||||||
store.dispatchNavigationAction(AppRouter::pop)
|
|
||||||
state.callback?.onAccept?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is DisclaimerAction.OnBackPressed -> {
|
|
||||||
store.dispatchNavigationAction(AppRouter::pop)
|
|
||||||
state.callback?.onDismiss?.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.redux
|
|
||||||
|
|
||||||
import com.tangem.tap.common.redux.AppState
|
|
||||||
import org.rekotlin.Action
|
|
||||||
|
|
||||||
object DisclaimerReducer {
|
|
||||||
fun reduce(action: Action, state: AppState): DisclaimerState = internalReduce(action, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun internalReduce(action: Action, state: AppState): DisclaimerState {
|
|
||||||
if (action !is DisclaimerAction) return state.disclaimerState
|
|
||||||
|
|
||||||
val disclaimerState = state.disclaimerState
|
|
||||||
|
|
||||||
return when (action) {
|
|
||||||
is DisclaimerAction.SetDisclaimer -> disclaimerState.copy(
|
|
||||||
disclaimer = action.disclaimer,
|
|
||||||
)
|
|
||||||
is DisclaimerAction.Show -> disclaimerState.copy(
|
|
||||||
showedFrom = action.from,
|
|
||||||
callback = action.callback,
|
|
||||||
)
|
|
||||||
is DisclaimerAction.AcceptDisclaimer, is DisclaimerAction.OnBackPressed -> disclaimerState.copy(
|
|
||||||
callback = null,
|
|
||||||
progressState = null,
|
|
||||||
)
|
|
||||||
is DisclaimerAction.OnProgressStateChanged -> disclaimerState.copy(
|
|
||||||
progressState = action.state,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.redux
|
|
||||||
|
|
||||||
enum class DisclaimerSource {
|
|
||||||
Home, Details
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.redux
|
|
||||||
|
|
||||||
import com.tangem.common.extensions.VoidCallback
|
|
||||||
|
|
||||||
import com.tangem.tap.common.entities.ProgressState
|
|
||||||
import com.tangem.tap.features.disclaimer.Disclaimer
|
|
||||||
import com.tangem.tap.features.disclaimer.DummyDisclaimer
|
|
||||||
import org.rekotlin.StateType
|
|
||||||
|
|
||||||
data class DisclaimerState(
|
|
||||||
val disclaimer: Disclaimer = DummyDisclaimer(),
|
|
||||||
val showedFrom: DisclaimerSource = DisclaimerSource.Home,
|
|
||||||
val callback: DisclaimerCallback? = null,
|
|
||||||
val progressState: ProgressState? = null,
|
|
||||||
) : StateType
|
|
||||||
|
|
||||||
data class DisclaimerCallback(
|
|
||||||
val onAccept: VoidCallback? = null,
|
|
||||||
val onDismiss: VoidCallback? = null,
|
|
||||||
)
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.ui
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import android.view.View.OVER_SCROLL_NEVER
|
|
||||||
import android.webkit.WebView
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.transition.TransitionInflater
|
|
||||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
|
||||||
|
|
||||||
import com.tangem.core.ui.extensions.setStatusBarColor
|
|
||||||
import com.tangem.tap.common.entities.ProgressState
|
|
||||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
|
||||||
import com.tangem.tap.common.extensions.hide
|
|
||||||
import com.tangem.tap.common.extensions.show
|
|
||||||
import com.tangem.tap.common.redux.AppState
|
|
||||||
import com.tangem.tap.features.BaseFragment
|
|
||||||
import com.tangem.tap.features.addBackPressHandler
|
|
||||||
import com.tangem.tap.features.disclaimer.Disclaimer
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerSource
|
|
||||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
|
|
||||||
import com.tangem.tap.store
|
|
||||||
import com.tangem.wallet.R
|
|
||||||
import com.tangem.wallet.databinding.FragmentDisclaimerBinding
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.rekotlin.StoreSubscriber
|
|
||||||
|
|
||||||
class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubscriber<DisclaimerState> {
|
|
||||||
|
|
||||||
private val binding: FragmentDisclaimerBinding by viewBinding(FragmentDisclaimerBinding::bind)
|
|
||||||
private val webViewClient = DisclaimerWebViewClient()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
addBackPressHandler(handler = this)
|
|
||||||
|
|
||||||
binding.apply {
|
|
||||||
toolbar.setNavigationOnClickListener { handleOnBackPressed() }
|
|
||||||
webView.apply {
|
|
||||||
settings.allowFileAccess = false
|
|
||||||
settings.javaScriptEnabled = false
|
|
||||||
overScrollMode = OVER_SCROLL_NEVER
|
|
||||||
webViewClient = this@DisclaimerFragment.webViewClient
|
|
||||||
}
|
|
||||||
webView.hide()
|
|
||||||
groupError.hide()
|
|
||||||
groupAccept.hide()
|
|
||||||
groupLoading.hide()
|
|
||||||
|
|
||||||
btnAccept.setOnClickListener {
|
|
||||||
store.dispatch(DisclaimerAction.AcceptDisclaimer)
|
|
||||||
}
|
|
||||||
btnRepeat.setOnClickListener {
|
|
||||||
webViewClient.reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
setStatusBarColor(R.color.background_secondary)
|
|
||||||
|
|
||||||
webViewClient.onProgressStateChanged = { store.dispatch(DisclaimerAction.OnProgressStateChanged(it)) }
|
|
||||||
store.subscribe(subscriber = this) { state ->
|
|
||||||
state
|
|
||||||
.skipRepeats { oldState, newState -> oldState.disclaimerState == newState.disclaimerState }
|
|
||||||
.select(AppState::disclaimerState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
webViewClient.onProgressStateChanged = null
|
|
||||||
store.unsubscribe(this)
|
|
||||||
super.onStop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun configureTransitions() {
|
|
||||||
val inflater = TransitionInflater.from(requireContext())
|
|
||||||
when (store.state.disclaimerState.showedFrom) {
|
|
||||||
DisclaimerSource.Home -> {
|
|
||||||
enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom)
|
|
||||||
exitTransition = inflater.inflateTransition(android.R.transition.slide_top)
|
|
||||||
}
|
|
||||||
DisclaimerSource.Details -> {
|
|
||||||
super.configureTransitions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun handleOnBackPressed() {
|
|
||||||
store.dispatch(DisclaimerAction.OnBackPressed)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun newState(state: DisclaimerState) {
|
|
||||||
return with(binding) {
|
|
||||||
if (activity == null || view == null) return
|
|
||||||
|
|
||||||
updateUiVisibility(state.disclaimer, state.progressState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateUiVisibility(disclaimer: Disclaimer, progressState: ProgressState?) = with(binding) {
|
|
||||||
when (progressState) {
|
|
||||||
ProgressState.Loading -> {
|
|
||||||
root.beginDelayedTransition()
|
|
||||||
webView.hide()
|
|
||||||
groupError.hide()
|
|
||||||
groupAccept.hide()
|
|
||||||
groupLoading.show()
|
|
||||||
}
|
|
||||||
ProgressState.Done -> {
|
|
||||||
root.beginDelayedTransition()
|
|
||||||
groupError.hide()
|
|
||||||
groupLoading.hide()
|
|
||||||
webView.show()
|
|
||||||
lifecycleScope.launch {
|
|
||||||
groupAccept.show(!disclaimer.isAccepted())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProgressState.Error -> {
|
|
||||||
root.beginDelayedTransition()
|
|
||||||
webView.show()
|
|
||||||
groupAccept.show()
|
|
||||||
groupLoading.hide()
|
|
||||||
groupError.hide()
|
|
||||||
webView.loadLocalTermsOfServices()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
webView.setBackgroundColor(resources.getColor(R.color.transparent, null))
|
|
||||||
webView.loadUrl(disclaimer.getUri().toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun WebView.loadLocalTermsOfServices() {
|
|
||||||
loadData(localTermsOfServices, "text/html", "UTF-8")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.ui
|
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.webkit.*
|
|
||||||
import com.tangem.common.extensions.ifNotNull
|
|
||||||
import com.tangem.tap.common.entities.ProgressState
|
|
||||||
|
|
||||||
class DisclaimerWebViewClient : WebViewClient() {
|
|
||||||
|
|
||||||
var onProgressStateChanged: ((ProgressState) -> Unit)? = null
|
|
||||||
|
|
||||||
private var loadingUrl: String? = null
|
|
||||||
private var loadedUrl: String? = null
|
|
||||||
|
|
||||||
private var progressState: ProgressState = ProgressState.Loading
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
onProgressStateChanged?.invoke(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reset() {
|
|
||||||
loadingUrl = null
|
|
||||||
loadedUrl = null
|
|
||||||
progressState = ProgressState.Loading
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
|
|
||||||
super.onPageStarted(view, url, favicon)
|
|
||||||
|
|
||||||
if (loadingUrl != url) progressState = ProgressState.Loading
|
|
||||||
loadingUrl = url
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPageFinished(view: WebView?, url: String?) {
|
|
||||||
super.onPageFinished(view, url)
|
|
||||||
|
|
||||||
if (loadedUrl != url) progressState = ProgressState.Done
|
|
||||||
loadedUrl = url
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReceivedError(view: WebView?, resourceRequest: WebResourceRequest?, error: WebResourceError?) {
|
|
||||||
super.onReceivedError(view, resourceRequest, error)
|
|
||||||
error?.let { progressState = ProgressState.Error }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReceivedHttpError(
|
|
||||||
view: WebView?,
|
|
||||||
resourceRequest: WebResourceRequest?,
|
|
||||||
errorResponse: WebResourceResponse?,
|
|
||||||
) {
|
|
||||||
super.onReceivedHttpError(view, resourceRequest, errorResponse)
|
|
||||||
|
|
||||||
ifNotNull(resourceRequest, errorResponse) { request, response ->
|
|
||||||
if (request.url?.toString() != loadingUrl || response.statusCode < RESPONSE_USER_ERROR_STATUS_CODE) return
|
|
||||||
if (progressState != ProgressState.Done) return
|
|
||||||
|
|
||||||
progressState = ProgressState.Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val RESPONSE_USER_ERROR_STATUS_CODE = 400
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
package com.tangem.tap.features.disclaimer.ui
|
|
||||||
|
|
||||||
internal val localTermsOfServices = """
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<title>Legal Disclaimer</title>
|
|
||||||
<style>
|
|
||||||
@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Light.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Light.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Light.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Light.ttf) format("truetype");font-weight:300;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Regular.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Regular.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Regular.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Regular.ttf) format("truetype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Medium.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Medium.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Medium.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Medium.ttf) format("truetype");font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Semibold.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Semibold.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Semibold.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Semibold.ttf) format("truetype");font-weight:600;font-style:normal;font-display:swap}@font-face{font-family:"SF Pro Display";src:url(/fonts/sf-pro/SFProDisplay-Bold.eot) format("embedded-opentype"),url(/fonts/sf-pro/SFProDisplay-Bold.woff2) format("woff2"),url(/fonts/sf-pro/SFProDisplay-Bold.woff) format("woff"),url(/fonts/sf-pro/SFProDisplay-Bold.ttf) format("truetype");font-weight:700;font-style:normal;font-display:swap}*{margin:0;padding:0}body{padding:0;margin:1rem;color:rgb(0 0 0);font-family:'SF Pro Display',-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}h1{font-weight:700;font-size:30px;line-height:1.2;margin-bottom:1rem}p{font-size:16px;line-height:20px;letter-spacing:-.24px;margin-bottom:1rem}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body><h1>Legal Disclaimer</h1>
|
|
||||||
<p> 1. Tangem application (Software)</p>
|
|
||||||
<p> The Software is intended for usage only with Tangem hardware wallets (Cards) via NFC interface. The
|
|
||||||
Software DOES NOT:</p>
|
|
||||||
<p> a) Generate, store, transmit, or have access to private (secret) cryptographic keys to blockchain
|
|
||||||
wallets holding digital assets, including crypto-currency.</p>
|
|
||||||
<p> b) Generate, store, transmit, or have access to secret keys, passwords, passphrases, recovery phrases
|
|
||||||
that can be used to restore or to copy private (secret) keys to blockchain wallets holding digital assets, including
|
|
||||||
crypto-currency.</p>
|
|
||||||
<p> c) Provide exchange, trading, investment services on behalf of Tangem AG.</p>
|
|
||||||
<p> 2. Risks related to the use of Software</p>
|
|
||||||
<p> Tangem will not be responsible for any losses, damages or claims arising from events falling within the
|
|
||||||
scope of the following five categories:</p>
|
|
||||||
<p> a) Mistakes made by the user of any cryptocurrency-related software or service, e.g., forgotten
|
|
||||||
passwords, payments sent to wrong addresses, and accidental deletion of blockchain wallets on Cards.</p>
|
|
||||||
<p> b) Problems of Software and/or any blockchain- or cryptocurrency- related software or service, e.g.,
|
|
||||||
corrupted files, incorrectly constructed transactions, unsafe cryptographic libraries, malware.</p>
|
|
||||||
<p> c) Technical failures in the hardware of the user, including Cards, of any cryptocurrency-related
|
|
||||||
software or service, e.g., data loss due to a faulty or damaged storage device.</p>
|
|
||||||
<p> d) Security problems experienced by the user of any cryptocurrency-related software or service, e.g.,
|
|
||||||
unauthorized access to users' wallets and/or accounts.</p>
|
|
||||||
<p> e) Actions or inactions of third parties and/or events experienced by third parties, e.g., bankruptcy
|
|
||||||
of service providers, information security attacks on service providers, and fraud conducted by third parties.</p>
|
|
||||||
<p> 3. Trading and Investment risks</p>
|
|
||||||
<p> There is considerable exposure to risk in any crypto-currency or other digital asset exchange
|
|
||||||
transaction. Any transaction involving currencies involves risks including, but not limited to, the potential for
|
|
||||||
changing economic conditions that may substantially affect the price or liquidity of a currency. Investments in
|
|
||||||
crypto-currency exchange speculation may also be susceptible to sharp rises and falls as the relevant market values
|
|
||||||
fluctuate. It is for this reason that when speculating in such markets it is advisable to use only risk capital.</p>
|
|
||||||
<p> 4. Electronic Trading Risks</p>
|
|
||||||
<p> Before you engage in transactions using an electronic system, you should carefully review the rules and
|
|
||||||
regulations of the exchanges offering the system and/or listing the instruments you intend to trade. Online trading
|
|
||||||
has inherent risk due to system response and access times that may vary due to market conditions, system
|
|
||||||
performance, and other factors. You should understand these and additional risks before trading.</p>
|
|
||||||
<p> 5. Compliance with tax obligations </p>
|
|
||||||
<p> The users of the Software are solely responsible to determinate what, if any, taxes apply to their
|
|
||||||
crypto-currency transactions. The owners of, or contributors to, the Software are NOT responsible for determining
|
|
||||||
the taxes that apply to crypto-currency transactions. </p>
|
|
||||||
<p> 6. No warranties </p>
|
|
||||||
<p> The Software is provided on an "as is" basis without any warranties of any kind regarding the
|
|
||||||
Software and/or any content, data, materials and/or services provided on the Software.
|
|
||||||
</p>
|
|
||||||
<p> 7. Limitation of liability</p>
|
|
||||||
<p> Unless otherwise required by law, in no event shall the owners of, or contributors to, the Software be
|
|
||||||
liable for any damages of any kind, including, but not limited to, loss of use, loss of profits, or loss of data
|
|
||||||
arising out of or in any way connected with the use of the Software. In no way are the owners of, or contributors
|
|
||||||
to, the Software responsible for the actions, decisions, or other behavior taken or not taken by you in reliance
|
|
||||||
upon the Software.</p>
|
|
||||||
<p> 8. Last amendment</p>
|
|
||||||
<p> This disclaimer was amended for the last time on October 1st, 2020.</p></body>
|
|
||||||
</html>
|
|
||||||
""".trimIndent()
|
|
||||||
|
|
@ -31,7 +31,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.feature.qrscanning.QrScanningRouter
|
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||||
import com.tangem.features.details.DetailsFeatureToggles
|
import com.tangem.features.details.DetailsFeatureToggles
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
|
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
|
||||||
import com.tangem.features.send.api.navigation.SendRouter
|
import com.tangem.features.send.api.navigation.SendRouter
|
||||||
import com.tangem.features.staking.api.navigation.StakingRouter
|
import com.tangem.features.staking.api.navigation.StakingRouter
|
||||||
|
|
@ -87,7 +86,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 pushNotificationsFeatureToggles: PushNotificationsFeatureToggles? = null,
|
|
||||||
val pushNotificationsRouter: PushNotificationsRouter? = null,
|
val pushNotificationsRouter: PushNotificationsRouter? = null,
|
||||||
val homeFeatureToggles: HomeFeatureToggles? = null,
|
val homeFeatureToggles: HomeFeatureToggles? = null,
|
||||||
val getUserCountryUseCase: GetUserCountryUseCase? = null,
|
val getUserCountryUseCase: GetUserCountryUseCase? = null,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import com.tangem.features.disclaimer.api.components.DisclaimerComponent
|
||||||
import com.tangem.features.managetokens.ManageTokensToggles
|
import com.tangem.features.managetokens.ManageTokensToggles
|
||||||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||||
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
|
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
|
import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter
|
||||||
import com.tangem.features.send.api.navigation.SendRouter
|
import com.tangem.features.send.api.navigation.SendRouter
|
||||||
import com.tangem.features.staking.api.navigation.StakingRouter
|
import com.tangem.features.staking.api.navigation.StakingRouter
|
||||||
|
|
@ -28,7 +27,6 @@ import com.tangem.tap.features.details.ui.details.DetailsFragment
|
||||||
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
|
import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment
|
||||||
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
|
import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment
|
||||||
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
|
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
|
||||||
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
|
|
||||||
import com.tangem.tap.features.home.HomeFragment
|
import com.tangem.tap.features.home.HomeFragment
|
||||||
import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment
|
import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment
|
||||||
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
|
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
|
||||||
|
|
@ -59,7 +57,6 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val stakingRouter: StakingRouter,
|
private val stakingRouter: StakingRouter,
|
||||||
private val testerRouter: TesterRouter,
|
private val testerRouter: TesterRouter,
|
||||||
private val detailsFeatureToggles: DetailsFeatureToggles,
|
private val detailsFeatureToggles: DetailsFeatureToggles,
|
||||||
private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles,
|
|
||||||
private val manageTokensToggles: ManageTokensToggles,
|
private val manageTokensToggles: ManageTokensToggles,
|
||||||
private val pushNotificationRouter: PushNotificationsRouter,
|
private val pushNotificationRouter: PushNotificationsRouter,
|
||||||
) {
|
) {
|
||||||
|
|
@ -111,15 +108,11 @@ internal class ChildFactory @Inject constructor(
|
||||||
route.asFragmentChild(Provider { SecurityModeFragment() })
|
route.asFragmentChild(Provider { SecurityModeFragment() })
|
||||||
}
|
}
|
||||||
is AppRoute.Disclaimer -> {
|
is AppRoute.Disclaimer -> {
|
||||||
if (pushNotificationsFeatureToggles.isPushNotificationsEnabled) {
|
route.asComponentChild(
|
||||||
route.asComponentChild(
|
contextProvider = contextProvider(route, contextFactory),
|
||||||
contextProvider = contextProvider(route, contextFactory),
|
params = DisclaimerComponent.Params(route.isTosAccepted),
|
||||||
params = DisclaimerComponent.Params(route.isTosAccepted),
|
componentFactory = disclaimerComponentFactory,
|
||||||
componentFactory = disclaimerComponentFactory,
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
route.asFragmentChild(Provider { DisclaimerFragment() })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is AppRoute.Home -> {
|
is AppRoute.Home -> {
|
||||||
route.asFragmentChild(Provider { HomeFragment() })
|
route.asFragmentChild(Provider { HomeFragment() })
|
||||||
|
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/coordinator_details_confirm"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/background_secondary"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/app_bar"
|
|
||||||
style="@style/Widget.MaterialComponents.Toolbar.Surface"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/background_secondary"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:liftOnScroll="true">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
|
|
||||||
app:navigationIconTint="@color/icon_primary_1"
|
|
||||||
app:titleTextColor="@color/text_primary_1"
|
|
||||||
app:title="@string/disclaimer_title" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/cl_details_confirm"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/background_secondary"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<WebView
|
|
||||||
android:id="@+id/web_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="74dp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/btn_accept"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/half_transparent_overlay"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="110dp"
|
|
||||||
android:background="@drawable/bg_half_transparent_overlay"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_error"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:lineSpacingExtra="5sp"
|
|
||||||
android:text="@string/disclaimer_error_loading"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/btn_repeat"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btn_repeat"
|
|
||||||
style="@style/TapSecondaryButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/common_retry"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tv_error" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_accept"
|
|
||||||
style="@style/TapPrimaryButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:text="@string/common_accept"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_error"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:constraint_referenced_ids="btn_repeat, tv_error" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_loading"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:constraint_referenced_ids="progress" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/group_accept"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:constraint_referenced_ids="btn_accept, half_transparent_overlay" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
@ -19,10 +19,6 @@
|
||||||
"name": "DETAILS_REDESIGN_ENABLED",
|
"name": "DETAILS_REDESIGN_ENABLED",
|
||||||
"version": "5.13.0"
|
"version": "5.13.0"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "PUSH_NOTIFICATIONS_ENABLED",
|
|
||||||
"version": "5.13.0"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "MARKETS_ENABLED",
|
"name": "MARKETS_ENABLED",
|
||||||
"version": "5.15.0"
|
"version": "5.15.0"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package com.tangem.features.pushnotifications.api.featuretoggles
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Push notifications feature toggles
|
|
||||||
*/
|
|
||||||
interface PushNotificationsFeatureToggles {
|
|
||||||
/** Availability of push notifications */
|
|
||||||
val isPushNotificationsEnabled: Boolean
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
package com.tangem.features.pushnotifications.impl.di
|
|
||||||
|
|
||||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.features.pushnotifications.impl.featuretoggles.DefaultPushNotificationsFeatureToggles
|
|
||||||
import dagger.Module
|
|
||||||
import dagger.Provides
|
|
||||||
import dagger.hilt.InstallIn
|
|
||||||
import dagger.hilt.components.SingletonComponent
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DI module provides implementation of [PushNotificationsFeatureToggles]
|
|
||||||
*/
|
|
||||||
@Module
|
|
||||||
@InstallIn(SingletonComponent::class)
|
|
||||||
internal object PushNotificationsFeatureTogglesModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideSendFeatureToggles(featureTogglesManager: FeatureTogglesManager): PushNotificationsFeatureToggles {
|
|
||||||
return DefaultPushNotificationsFeatureToggles(featureTogglesManager = featureTogglesManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package com.tangem.features.pushnotifications.impl.featuretoggles
|
|
||||||
|
|
||||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
|
|
||||||
internal class DefaultPushNotificationsFeatureToggles(
|
|
||||||
private val featureTogglesManager: FeatureTogglesManager,
|
|
||||||
) : PushNotificationsFeatureToggles {
|
|
||||||
override val isPushNotificationsEnabled: Boolean
|
|
||||||
get() = featureTogglesManager.isFeatureEnabled("PUSH_NOTIFICATIONS_ENABLED")
|
|
||||||
}
|
|
||||||
|
|
@ -28,7 +28,6 @@ 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.utils.ScreenLifecycleProvider
|
||||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||||
import com.tangem.features.markets.MarketsFeatureToggles
|
import com.tangem.features.markets.MarketsFeatureToggles
|
||||||
import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles
|
|
||||||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
|
|
@ -67,7 +66,6 @@ internal class WalletViewModel @Inject constructor(
|
||||||
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
|
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
|
||||||
private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase,
|
private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase,
|
||||||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||||
private val pushNotificationsFeatureToggles: PushNotificationsFeatureToggles,
|
|
||||||
private val marketsFeatureToggles: MarketsFeatureToggles,
|
private val marketsFeatureToggles: MarketsFeatureToggles,
|
||||||
analyticsEventsHandler: AnalyticsEventHandler,
|
analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
@ -166,10 +164,9 @@ internal class WalletViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun subscribeOnPushNotificationsPermission() {
|
private fun subscribeOnPushNotificationsPermission() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val isPushToggled = pushNotificationsFeatureToggles.isPushNotificationsEnabled
|
|
||||||
val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION)
|
val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION)
|
||||||
val isPushPermissionAvailable = getPushPermissionOrNull() != null
|
val isPushPermissionAvailable = getPushPermissionOrNull() != null
|
||||||
if (!isPushToggled || !shouldRequestPush || !isPushPermissionAvailable) return@launch
|
if (!shouldRequestPush || !isPushPermissionAvailable) return@launch
|
||||||
|
|
||||||
delay(timeMillis = 1_800)
|
delay(timeMillis = 1_800)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue