diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b36d773b95..154659cd53 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -109,6 +109,8 @@ dependencies {
implementation(projects.domain.promo)
implementation(projects.domain.promo.models)
implementation(projects.domain.networks)
+ implementation(projects.domain.quotes)
+ implementation(projects.domain.notifications)
implementation(projects.common)
implementation(projects.common.routing)
@@ -152,9 +154,10 @@ dependencies {
implementation(projects.data.nft)
implementation(projects.data.onramp)
implementation(projects.data.networks)
+ implementation(projects.data.quotes)
+ implementation(projects.data.blockaid)
/** Features */
- implementation(projects.features.onboarding)
implementation(projects.features.referral.impl)
implementation(projects.features.referral.domain)
implementation(projects.features.referral.data)
@@ -273,6 +276,7 @@ dependencies {
implementation(deps.reKotlin)
implementation(deps.zxing.qrCore)
implementation(deps.coil)
+ implementation(deps.coil.gif)
implementation(deps.amplitude)
implementation(deps.kotsonGson)
implementation(deps.spongecastle.core)
@@ -318,6 +322,7 @@ dependencies {
/** Chucker */
debugImplementation(deps.chucker)
mockedImplementation(deps.chuckerStub)
+ externalImplementation(deps.chuckerStub)
internalImplementation(deps.chuckerStub)
releaseImplementation(deps.chuckerStub)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 21808cc1b9..cac33d689c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -43,6 +43,7 @@
android:resizeableActivity="@bool/resizeable_activity"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:allowBackup, android:fullBackupContent, android:label">
@@ -127,7 +128,7 @@
diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config
index 29f73d909e..a20134ee4b 160000
--- a/app/src/main/assets/tangem-app-config
+++ b/app/src/main/assets/tangem-app-config
@@ -1 +1 @@
-Subproject commit 29f73d909e14cb3760118b197b1929a6088bc40e
+Subproject commit a20134ee4be4d7e1e34c6f44089b3cb0713eacc3
diff --git a/app/src/main/java/com/tangem/tap/DecomposeFragment.kt b/app/src/main/java/com/tangem/tap/DecomposeFragment.kt
deleted file mode 100644
index 3136ae77dd..0000000000
--- a/app/src/main/java/com/tangem/tap/DecomposeFragment.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.tangem.tap
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import com.tangem.core.decompose.context.AppComponentContext
-import com.tangem.core.decompose.factory.ComponentFactory
-import com.tangem.core.ui.UiDependencies
-import com.tangem.core.ui.decompose.ComposableContentComponent
-import com.tangem.core.ui.screen.ComposeFragment
-import com.tangem.utils.Provider
-import dagger.hilt.android.AndroidEntryPoint
-import java.util.WeakHashMap
-import javax.inject.Inject
-
-@AndroidEntryPoint
-internal class DecomposeFragment : ComposeFragment() {
-
- @Inject
- override lateinit var uiDependencies: UiDependencies
-
- private val component by lazy(LazyThreadSafetyMode.NONE) {
- val tag = requireArguments().getString(TAG_KEY)
- val builder = componentsBuilders[tag]
-
- requireNotNull(builder?.build()) {
- "Component builder is not set, call newInstance() for DecomposeFragment creation first."
- }
- }
-
- @Composable
- override fun ScreenContent(modifier: Modifier) {
- component.Content(modifier)
- }
-
- private class ComponentBuilder>(
- private val contextProvider: Provider,
- private val params: P,
- private val componentFactory: F,
- ) {
-
- fun build(): C = componentFactory.create(contextProvider(), params)
- }
-
- companion object {
-
- private const val TAG_KEY = "tag"
-
- private val componentsBuilders = WeakHashMap>()
-
- fun > newInstance(
- tag: String,
- contextProvider: Provider,
- params: P,
- componentFactory: F,
- ): Fragment {
- this@Companion.componentsBuilders[tag] = ComponentBuilder(contextProvider, params, componentFactory)
-
- return DecomposeFragment().apply {
- this.arguments = bundleOf(TAG_KEY to tag)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt
index 3eb6102ce8..66df0f2d73 100644
--- a/app/src/main/java/com/tangem/tap/MainActivity.kt
+++ b/app/src/main/java/com/tangem/tap/MainActivity.kt
@@ -12,7 +12,6 @@ import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
-import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
@@ -20,19 +19,11 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
-import by.kirich1409.viewbindingdelegate.viewBinding
-import com.arkivanov.decompose.value.subscribe
-import com.arkivanov.essenty.lifecycle.asEssentyLifecycle
-import com.google.android.material.snackbar.BaseTransientBottomBar
-import com.google.android.material.snackbar.Snackbar
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.entity.SerializableIntent
import com.tangem.core.analytics.api.AnalyticsEventHandler
@@ -41,12 +32,6 @@ import com.tangem.core.decompose.di.RootAppComponentContext
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.navigation.email.EmailSender
import com.tangem.core.ui.UiDependencies
-import com.tangem.core.ui.extensions.TextReference
-import com.tangem.core.ui.extensions.resolveReference
-import com.tangem.core.ui.message.EventMessageEffect
-import com.tangem.core.ui.message.SnackbarMessage
-import com.tangem.core.ui.res.TangemColorPalette
-import com.tangem.core.ui.res.TangemTheme
import com.tangem.data.balancehiding.DefaultDeviceFlipDetector
import com.tangem.data.card.sdk.CardSdkOwner
import com.tangem.domain.apptheme.model.AppThemeMode
@@ -62,37 +47,28 @@ import com.tangem.domain.tokens.GetPolkadotCheckHasImmortalUseCase
import com.tangem.domain.tokens.GetPolkadotCheckHasResetUseCase
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
-import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.google.GoogleServicesHelper
import com.tangem.operations.backup.BackupService
import com.tangem.sdk.api.BackupServiceHolder
import com.tangem.sdk.api.TangemSdkManager
-import com.tangem.sdk.extensions.init
import com.tangem.tap.common.ActivityResultCallbackHolder
import com.tangem.tap.common.DialogManager
import com.tangem.tap.common.OnActivityResultCallback
-import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
import com.tangem.tap.common.extensions.dispatchNavigationAction
-import com.tangem.tap.common.extensions.showFragmentAllowingStateLoss
-import com.tangem.tap.common.redux.NotificationsHandler
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
import com.tangem.tap.features.intentHandler.IntentProcessor
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.main.MainViewModel
-import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.configurator.AppRouterConfig
-import com.tangem.tap.routing.toggle.RoutingFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
-import com.tangem.wallet.R
-import com.tangem.wallet.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@@ -105,7 +81,6 @@ lateinit var tangemSdkManager: TangemSdkManager
lateinit var backupService: BackupService
internal var lockUserWalletsTimer: LockUserWalletsTimer? = null
private set
-var notificationsHandler: NotificationsHandler? = null
private val coroutineContext: CoroutineContext
get() = SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("scope")
@@ -117,7 +92,7 @@ val mainScope = CoroutineScope(mainCoroutineContext)
@Suppress("LargeClass")
@AndroidEntryPoint
-class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbackHolder {
+class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder {
@Inject
lateinit var appStateHolder: AppStateHolder
@@ -181,9 +156,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var backupServiceHolder: BackupServiceHolder
- @Inject
- lateinit var onboardingV2FeatureToggles: OnboardingV2FeatureToggles
-
@Inject
lateinit var setGoogleServicesAvailabilityUseCase: SetGoogleServicesAvailabilityUseCase
@@ -193,9 +165,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var dispatchers: CoroutineDispatcherProvider
- @Inject
- internal lateinit var routingFeatureToggles: RoutingFeatureToggles
-
@Inject
internal lateinit var uiDependencies: UiDependencies
@@ -209,9 +178,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
// TODO: fixme: inject through DI
private val intentProcessor: IntentProcessor = IntentProcessor()
- private var snackbar: Snackbar? = null
private val dialogManager = DialogManager()
- private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::bind)
private val onActivityResultCallbacks = mutableListOf()
@@ -248,13 +215,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
installActivityDependencies()
observeAppThemeModeUpdates()
- if (routingFeatureToggles.isNavigationRefactoringEnabled) {
- setRootContent()
- } else {
- setContentView(R.layout.activity_main)
- installRouting()
- installEventMessageEffect()
- }
+ setRootContent()
initContent()
@@ -271,33 +232,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lifecycle.addObserver(defaultDeviceFlipDetector)
}
- private fun installEventMessageEffect() {
- binding.composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- binding.composeView.setContent {
- TangemTheme(
- activity = this,
- uiDependencies = uiDependencies,
- overrideSystemBarColors = false,
- ) {
- EventMessageEffect(
- onShowSnackbar = { message, _ ->
- showSnackbar(
- text = message.message.resolveReference(resources),
- length = when (message.duration) {
- SnackbarMessage.Duration.Short -> Snackbar.LENGTH_SHORT
- SnackbarMessage.Duration.Long -> Snackbar.LENGTH_LONG
- SnackbarMessage.Duration.Indefinite -> Snackbar.LENGTH_INDEFINITE
- },
- buttonTitle = message.actionLabel?.resolveReference(resources),
- action = message.action,
- onDismiss = message.onDismissRequest,
- )
- },
- )
- }
- }
- }
-
private fun setRootContent() {
// for now activity is singleTop and after going to ChromeCustomTab it calls onCreate but onDestroy
// doesn't calls. It lead to issue that decompose nav stack is not saved in bundle and to restore it
@@ -312,64 +246,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
- private fun installRouting() {
- // for now activity is singleTop and after going to ChromeCustomTab it calls onCreate but onDestroy
- // doesn't calls. It lead to issue that decompose nav stack is not saved in bundle and to restore it
- // we try to init component with previous stack
- val routingComponent = routingComponentFactory.create(
- context = rootComponentContext,
- initialStack = appRouterConfig.stack,
- )
-
- appRouterConfig.routerScope = mainScope
- appRouterConfig.componentRouter = routingComponent.router
- appRouterConfig.snackbarHandler = this
-
- routingComponent.stack.subscribe(lifecycle.asEssentyLifecycle()) { childStack ->
- val stack = childStack.backStack
- .plus(childStack.active)
- .map { it.configuration }
-
- if (stack == appRouterConfig.stack) return@subscribe
-
- appRouterConfig.stack = stack
-
- if (stack.size == 1) {
- supportFragmentManager.run {
- val activeChildName = stack.first().path
-
- (0 until backStackEntryCount)
- .mapNotNull { getBackStackEntryAt(it).name }
- .filter { it != activeChildName }
- .forEach {
- popBackStackImmediate(it, POP_BACK_STACK_INCLUSIVE)
- }
- }
- }
-
- when (val child = childStack.active.instance) {
- is RoutingComponent.Child.Initial -> Unit
- is RoutingComponent.Child.LegacyFragment -> {
- supportFragmentManager.showFragmentAllowingStateLoss(child.name, child.fragmentProvider)
- }
- is RoutingComponent.Child.LegacyIntent -> {
- startActivity(child.intent)
- }
- is RoutingComponent.Child.ComposableComponent -> error("Unsupported child: $child")
- }
- }
- }
-
private fun installActivityDependencies() {
cardSdkOwner.register(activity = this)
tangemSdkManager = injectedTangemSdkManager
- if (onboardingV2FeatureToggles.isOnboardingV2Enabled) {
- backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this)
- backupService = backupServiceHolder.backupService.get()!! // will be deleted eventually
- } else {
- backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
- }
+ backupServiceHolder.createAndSetService(cardSdkConfigRepository.sdk, this)
+ backupService = backupServiceHolder.backupService.get()!! // will be deleted eventually
lockUserWalletsTimer = LockUserWalletsTimer(
context = this,
@@ -439,17 +321,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
override fun onResume() {
super.onResume()
-
- if (!routingFeatureToggles.isNavigationRefactoringEnabled) {
- // TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
- notificationsHandler = NotificationsHandler(binding.fragmentContainer)
- }
-
navigateToInitialScreenIfNeeded(intent)
}
override fun onStop() {
- notificationsHandler = null
dialogManager.onStop()
super.onStop()
}
@@ -482,31 +357,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
MutableAppThemeModeHolder.isDarkThemeActive = isDarkTheme()
}
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
-
- /*
- * We need to manually change the background color of the activity when the UI mode changes to prevent
- * flickering when navigating between fragments.
- *
-
- * `android:configChanges="uiMode"` is set in the manifest.
- * */
- if (routingFeatureToggles.isNavigationRefactoringEnabled.not()) {
- updateAppBackground()
- }
- }
-
- private fun updateAppBackground() {
- val backgroundColor = if (isDarkTheme()) {
- TangemColorPalette.Dark6
- } else {
- TangemColorPalette.White
- }
-
- findViewById(R.id.fragment_container).setBackgroundColor(backgroundColor.toArgb())
- }
-
private fun isDarkTheme(): Boolean {
return when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES -> true
@@ -528,29 +378,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
- override fun showSnackbar(@StringRes text: Int, length: Int, @StringRes buttonTitle: Int?, action: (() -> Unit)?) {
- showSnackbar(
- getString(text),
- length,
- buttonTitle?.let(::getString),
- action = { action?.invoke() },
- )
- }
-
- override fun showSnackbar(text: TextReference, length: Int, buttonTitle: TextReference?, action: (() -> Unit)?) {
- showSnackbar(
- text = text.resolveReference(resources),
- length = length,
- buttonTitle = buttonTitle?.resolveReference(resources),
- action = { action?.invoke() },
- )
- }
-
- override fun dismissSnackbar() {
- snackbar?.dismiss()
- snackbar = null
- }
-
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
@@ -578,40 +405,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
return if (result) super.dispatchTouchEvent(event) else false
}
- private fun showSnackbar(
- text: String,
- length: Int,
- buttonTitle: String?,
- action: (() -> Unit)? = null,
- onDismiss: () -> Unit = {},
- ) {
- if (snackbar != null) return
-
- snackbar = Snackbar.make(binding.fragmentContainer, text, length).apply {
- val textColor = getColor(R.color.text_primary_2)
-
- setBackgroundTint(getColor(R.color.button_primary))
- setActionTextColor(textColor)
- setTextColor(textColor)
-
- if (buttonTitle != null && action != null) {
- setAction(buttonTitle, { action() })
- }
-
- addCallback(
- object : BaseTransientBottomBar.BaseCallback() {
- override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
- onDismiss()
- snackbar = null
- removeCallback(this)
- }
- },
- )
- }
-
- snackbar?.show()
- }
-
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
val backStack = appRouterConfig.stack ?: emptyList()
// TODO move inital navigation to navigation component ([REDACTED_JIRA])
@@ -662,7 +455,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
- store.dispatch(BackupAction.CheckForUnfinishedBackup)
+ viewModel.checkForUnfinishedBackup()
}
private fun observePolkadotAccountHealthCheck() {
diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt
index 4cc52e4409..d61bb4e130 100644
--- a/app/src/main/java/com/tangem/tap/TangemApplication.kt
+++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt
@@ -1,6 +1,9 @@
package com.tangem.tap
import android.app.Application
+import android.os.StrictMode
+import android.os.StrictMode.ThreadPolicy
+import android.os.StrictMode.VmPolicy
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import coil.ImageLoader
@@ -71,9 +74,7 @@ import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.wallet.BuildConfig
import dagger.hilt.EntryPoints
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.*
import org.rekotlin.Store
import kotlin.collections.set
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository as WalletConnect2Repository
@@ -228,12 +229,32 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
// endregion
+ private val appScope = MainScope()
+
override fun onCreate() {
+ enableStrictModeInDebug()
super.onCreate()
-
init()
+ }
- updateLogFiles()
+ private fun enableStrictModeInDebug() {
+ if (BuildConfig.DEBUG) {
+ StrictMode.setThreadPolicy(
+ ThreadPolicy.Builder()
+ .detectDiskReads()
+ .detectDiskWrites()
+ .detectAll()
+ .penaltyLog()
+ .build(),
+ )
+ StrictMode.setVmPolicy(
+ VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .detectLeakedClosableObjects()
+ .penaltyLog()
+ .build(),
+ )
+ }
}
private fun updateLogFiles() {
@@ -260,18 +281,28 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
foregroundActivityObserver = ForegroundActivityObserver()
registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks)
- // TODO: Try to performance and user experience.
- // [REDACTED_JIRA]
+ // We need to initialize the toggles and excludedBlockchainsManager before the MainActivity starts using them.
runBlocking {
awaitAll(
- async { featureTogglesManager.init() },
- async { excludedBlockchainsManager.init() },
- async { initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize()) },
+ async {
+ featureTogglesManager.init()
+ },
+ async {
+ excludedBlockchainsManager.init()
+ },
)
+ initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
+ }
+
+ appScope.launch {
+ launch(Dispatchers.IO) {
+ loadNativeLibraries()
+ updateLogFiles()
+ }
}
- loadNativeLibraries()
ExceptionHandler.append(blockchainExceptionHandler)
+
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
@@ -287,9 +318,11 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
appPreferencesStore = appPreferencesStore,
dispatchers = dispatchers,
)
- appStateHolder.mainStore = store
- walletConnect2Repository.init(projectId = environmentConfigStorage.getConfigSync().walletConnectProjectId)
+ appStateHolder.mainStore = store
+ walletConnect2Repository.init(
+ projectId = environmentConfigStorage.getConfigSync().walletConnectProjectId,
+ )
}
private fun createReduxStore(): Store {
diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
index 533cdac6a9..814384c569 100644
--- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt
+++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
@@ -9,9 +9,11 @@ import com.tangem.tap.common.ui.*
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
import com.tangem.tap.features.onboarding.OnboardingDialog
-import com.tangem.tap.features.onboarding.products.twins.ui.dialog.TwinningProcessNotCompletedDialog
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
-import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.*
+import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
+import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
+import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletActivationErrorDialog
+import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletAlreadyWasUsedDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import org.rekotlin.StoreSubscriber
@@ -56,10 +58,6 @@ class DialogManager : StoreSubscriber {
messageRes = R.string.nfc_error_unavailable,
context = context,
)
- is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
- is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
- is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context)
- is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
is OnboardingDialog.WalletActivationError -> WalletActivationErrorDialog.create(context, state.dialog)
is WalletConnectDialog.UnsupportedCard ->
SimpleAlertDialog.create(
@@ -143,9 +141,6 @@ class DialogManager : StoreSubscriber {
messageRes = R.string.unsupported_wc_version,
context = context,
)
- is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context)
- is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
- is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(
context = context,
scanResponse = state.dialog.scanResponse,
@@ -154,10 +149,6 @@ class DialogManager : StoreSubscriber {
context = context,
unfinishedBackupScanResponse = state.dialog.scanResponse,
)
- is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create(
- context = context,
- cardId = state.dialog.cardId,
- )
is AppDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create(
title = context.getString(state.dialog.titleRes, state.dialog.currencySymbol),
message = context.getString(
diff --git a/app/src/main/java/com/tangem/tap/common/TestActions.kt b/app/src/main/java/com/tangem/tap/common/TestActions.kt
index 955b89e867..5dc923cf58 100644
--- a/app/src/main/java/com/tangem/tap/common/TestActions.kt
+++ b/app/src/main/java/com/tangem/tap/common/TestActions.kt
@@ -1,16 +1,5 @@
package com.tangem.tap.common
-import android.content.Context
-import android.os.Bundle
-import android.view.View
-import android.widget.Button
-import androidx.appcompat.widget.LinearLayoutCompat
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.tangem.tap.common.extensions.dispatchDialogHide
-import com.tangem.tap.common.redux.AppDialog
-import com.tangem.tap.store
-
/**
[REDACTED_AUTHOR]
*/
@@ -21,41 +10,4 @@ object TestActions {
var testAmountInjectionForWalletManagerEnabled = false
}
-typealias TestAction = Pair Unit>
-
-class TestActionsBottomSheetDialog(
- private val appDialog: AppDialog.TestActionsDialog,
- context: Context,
-) : BottomSheetDialog(context) {
-
- @Suppress("MagicNumber")
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContentView(createContentView())
- behavior.state = BottomSheetBehavior.STATE_EXPANDED
- behavior.peekHeight = 400
- setOnCancelListener {
- store.dispatchDialogHide()
- }
- }
-
- private fun createContentView(): View {
- val actionButtons = mutableListOf()
- appDialog.actionsList.forEach { (actionName, action) ->
- val view = Button(context).apply {
- text = actionName
- setOnClickListener {
- action()
- cancel()
- }
- }
- actionButtons.add(view)
- }
-
- return LinearLayoutCompat(context).apply {
- orientation = LinearLayoutCompat.VERTICAL
- actionButtons.forEach { addView(it) }
- }
- }
-}
\ No newline at end of file
+typealias TestAction = Pair Unit>
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
deleted file mode 100644
index 243a2f8770..0000000000
--- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.tangem.tap.common.extensions
-
-import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import com.tangem.utils.Provider
-import com.tangem.wallet.R
-import timber.log.Timber
-
-fun FragmentManager.showFragmentAllowingStateLoss(name: String, fragmentProvider: Provider) {
- if (backStackEntryCount > 0) {
- val currentFragmentName = getBackStackEntryAt(backStackEntryCount - 1).name
-
- if (name == currentFragmentName) {
- Timber.i("Fragment $name is already at the top of the stack")
- return
- }
- }
-
- Timber.i("Showing $name route")
-
- val isPoppedBack = popBackStackImmediate(name, 0)
-
- if (!isPoppedBack) {
- val fragment = fragmentProvider()
-
- if (fragment is DialogFragment) {
- fragment.showDialog(fragmentManager = this, name)
- } else {
- fragment.showFragment(fragmentManager = this, name)
- }
-
- Timber.i("Route $name is shown")
- } else {
- Timber.i("Route $name is found in backstack and shown")
- }
-}
-
-private fun DialogFragment.showDialog(fragmentManager: FragmentManager, name: String) {
- val transaction = fragmentManager.beginTransaction()
-
- try {
- transaction.addToBackStack(name)
- show(transaction, name)
- } catch (e: IllegalStateException) {
- transaction.add(this, name)
- transaction.addToBackStack(name)
-
- transaction.commitAllowingStateLoss()
- }
-}
-
-private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String) {
- val transaction = fragmentManager.beginTransaction()
-
- transaction.replace(R.id.fragment_container, this, name)
- transaction.addToBackStack(name)
-
- transaction.commitAllowingStateLoss()
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
index 919c21972d..c865434607 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt
@@ -1,6 +1,8 @@
package com.tangem.tap.common.extensions
import com.tangem.common.routing.AppRouter
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.wallets.models.UserWallet
@@ -43,20 +45,16 @@ suspend fun Store.onUserWalletSelected(userWallet: UserWallet) {
state.globalState.tapWalletManager.onWalletSelected(userWallet)
}
-fun Store<*>.dispatchErrorNotification(error: TapError) {
- dispatchOnMain(GlobalAction.ShowErrorNotification(error))
-}
-
/**
* @param fatal used to indicate errors that should not normally occur
*/
-fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
+fun Store.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: "
dispatchDebugErrorNotification(TapError.CustomError("$prefix $message"))
}
-fun Store<*>.dispatchDebugErrorNotification(error: TapError) {
- dispatchOnMain(GlobalAction.DebugShowErrorNotification(error))
+fun Store.dispatchDebugErrorNotification(error: TapError) {
+ inject(DaggerGraphState::uiMessageSender).send(SnackbarMessage(stringReference(error.message ?: "debug error")))
}
fun Store<*>.dispatchDialogShow(dialog: StateDialog) {
diff --git a/app/src/main/java/com/tangem/tap/common/images/Coil.kt b/app/src/main/java/com/tangem/tap/common/images/Coil.kt
index 4b269bdca4..28336b233f 100644
--- a/app/src/main/java/com/tangem/tap/common/images/Coil.kt
+++ b/app/src/main/java/com/tangem/tap/common/images/Coil.kt
@@ -1,8 +1,11 @@
package com.tangem.tap.common.images
import android.content.Context
+import android.os.Build
import android.util.Log
import coil.ImageLoader
+import coil.decode.GifDecoder
+import coil.decode.ImageDecoderDecoder
import coil.memory.MemoryCache
import coil.request.CachePolicy
import coil.util.Logger
@@ -25,6 +28,15 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
.build()
}
}
+ .components {
+ // According to Coil ImageDecoder API is faster and supports animated WebP and HEIF
+ // https://coil-kt.github.io/coil/gifs/
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ add(ImageDecoderDecoder.Factory())
+ } else {
+ add(GifDecoder.Factory())
+ }
+ }
.memoryCachePolicy(CachePolicy.ENABLED)
.memoryCache {
MemoryCache.Builder(context)
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
index 1188cd7d03..8c9f98ebdd 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
@@ -2,9 +2,6 @@ package com.tangem.tap.common.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.redux.StateDialog
-import com.tangem.tap.common.TestAction
-import com.tangem.tap.domain.model.Currency
-import com.tangem.tap.domain.model.WalletAddressData
import com.tangem.wallet.R
/**
@@ -18,15 +15,6 @@ sealed class AppDialog : StateDialog {
val onOk: VoidCallback? = null,
) : AppDialog()
- internal data class AddressInfoDialog(
- val currency: Currency,
- val addressData: WalletAddressData,
- ) : AppDialog()
-
- data class TestActionsDialog(
- val actionsList: List,
- ) : AppDialog()
-
data class RemoveWalletDialog(
val currencyTitle: String,
val onOk: () -> Unit,
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
index 80b48b4193..111bad630c 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt
@@ -4,11 +4,6 @@ import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
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.otherCards.redux.OnboardingOtherCardsReducer
-import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer
-import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
-import com.tangem.tap.features.saveWallet.redux.SaveWalletReducer
import com.tangem.tap.features.welcome.redux.WelcomeReducer
import com.tangem.tap.proxy.redux.DaggerGraphReducer
import org.rekotlin.Action
@@ -20,14 +15,9 @@ fun appReducer(action: Action, state: AppState?): AppState {
return AppState(
globalState = globalReducer(action, state),
homeState = HomeReducer.reduce(action, state),
- onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
- onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
- onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
- twinCardsState = TwinCardsReducer.reduce(action, state),
detailsState = DetailsReducer.reduce(action, state),
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
welcomeState = WelcomeReducer.reduce(action, state),
- saveWalletState = SaveWalletReducer.reduce(action, state),
daggerGraphState = DaggerGraphReducer.reduce(action, state),
)
}
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
index 04d6fa4896..3df1fae79b 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt
@@ -9,17 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddlewa
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
import com.tangem.tap.features.home.redux.HomeMiddleware
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.OnboardingNoteState
-import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware
-import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
-import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsMiddleware
-import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware
-import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
-import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
-import com.tangem.tap.features.saveWallet.redux.SaveWalletMiddleware
-import com.tangem.tap.features.saveWallet.redux.SaveWalletState
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
import com.tangem.tap.features.welcome.redux.WelcomeState
@@ -31,14 +21,9 @@ import org.rekotlin.StateType
data class AppState(
val globalState: GlobalState = GlobalState(),
val homeState: HomeState = HomeState(),
- val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(),
- val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
- val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
- val twinCardsState: TwinCardsState = TwinCardsState(),
val detailsState: DetailsState = DetailsState(),
val walletConnectState: WalletConnectState = WalletConnectState(),
val welcomeState: WelcomeState = WelcomeState(),
- val saveWalletState: SaveWalletState = SaveWalletState(),
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
) : StateType {
@@ -46,18 +31,12 @@ data class AppState(
fun getMiddleware(): List> {
return listOf(
logMiddleware,
- notificationsMiddleware,
GlobalMiddleware.handler,
HomeMiddleware.handler,
- OnboardingNoteMiddleware.handler,
- OnboardingWalletMiddleware.handler,
- OnboardingOtherCardsMiddleware.handler,
- TwinCardsMiddleware.handler,
DetailsMiddleware().detailsMiddleware,
WalletConnectMiddleware().walletConnectMiddleware,
BackupMiddleware().backupMiddleware,
WelcomeMiddleware().middleware,
- SaveWalletMiddleware().middleware,
LockUserWalletsTimerMiddleware().middleware,
AccessCodeRequestPolicyMiddleware().middleware,
DaggerGraphMiddleware.daggerGraphMiddleware,
diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt
deleted file mode 100644
index 1b862e7ed4..0000000000
--- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package com.tangem.tap.common.redux
-
-import android.content.Context
-import android.widget.Toast
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import com.google.android.material.snackbar.Snackbar
-import com.tangem.tap.common.extensions.getColor
-import com.tangem.tap.domain.ArgError
-import com.tangem.tap.domain.MultiMessageError
-import com.tangem.tap.domain.TapError
-import com.tangem.tap.domain.assembleErrors
-import com.tangem.tap.notificationsHandler
-import com.tangem.wallet.BuildConfig
-import com.tangem.wallet.R
-import org.rekotlin.Action
-import org.rekotlin.Middleware
-import java.lang.ref.WeakReference
-
-class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
- private val basicCoordinatorLayout = WeakReference(coordinatorLayout)
- private var baseLayout = basicCoordinatorLayout
-
- private fun showNotification(message: String) {
- baseLayout.get()?.let { layout ->
- Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
- .also { snackbar -> snackbar.show() }
- }
- }
-
- private fun showDebugNotification(message: String) {
- baseLayout.get()?.let { layout ->
- Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
- .also { snackbar ->
- snackbar.setBackgroundTint(layout.getColor(R.color.warning_warning))
- snackbar.show()
- }
- }
- }
-
- fun showNotification(message: Int, args: List? = null) {
- baseLayout.get()?.let {
- showNotification(getMessageString(it.context, message, args))
- }
- }
-
- fun showToastNotification(message: Int, args: List? = null) {
- baseLayout.get()?.let {
- Toast.makeText(it.context, getMessageString(it.context, message, args), Toast.LENGTH_LONG).show()
- }
- }
-
- fun showNotification(errorList: List?>>, builder: (List) -> String) {
- val context = baseLayout.get()?.context ?: return
-
- val message = builder(errorList.map { getMessageString(context, it.first, it.second) })
- showNotification(message)
- }
-
- fun showDebugErrorNotification(message: Int, args: List? = null) {
- baseLayout.get()?.let {
- showDebugNotification(getMessageString(it.context, message, args))
- }
- }
-}
-
-fun getMessageString(context: Context, message: Int, args: List?): String {
- return if (args.isNullOrEmpty()) {
- context.getString(message)
- } else {
- context.getString(message, *args.toTypedArray())
- }
-}
-
-val notificationsMiddleware: Middleware = { dispatch, state ->
- { next ->
- { action ->
- handleNotificationAction(action)
- next(action)
- }
- }
-}
-
-@Suppress("ComplexMethod")
-private fun handleNotificationAction(action: Action) {
- if (action is Debug && !BuildConfig.DEBUG) return
-
- when (action) {
- is NotificationAction -> {
- notificationsHandler?.showNotification(action.messageResource)
- }
- is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource)
- is ErrorAction -> {
- when (action) {
- is Debug -> {
- val args = (action.error as? ArgError)?.args ?: listOf()
- when (action) {
- is DebugNotification -> {
- notificationsHandler?.showNotification(action.error.messageResource, args)
- }
- is DebugToastNotification -> {
- notificationsHandler?.showToastNotification(action.error.messageResource, args)
- }
- is DebugErrorAction -> {
- notificationsHandler?.showDebugErrorNotification(action.error.messageResource, args)
- }
- }
- }
- else -> {
- when (action.error) {
- is MultiMessageError -> {
- val multiError = action.error as MultiMessageError
- notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder)
- }
- else -> {
- val args = (action.error as? ArgError)?.args ?: listOf()
- notificationsHandler?.showNotification(action.error.messageResource, args)
- }
- }
- }
- }
- }
- }
-}
-
-interface ToastNotificationAction : Action {
- val messageResource: Int
-}
-
-interface NotificationAction : Action {
- val messageResource: Int
-}
-
-interface ErrorAction : Action {
- val error: TapError
-}
-
-// Processed only in the debug builds
-interface Debug
-interface DebugNotification : Debug, NotificationAction
-interface DebugToastNotification : Debug, ToastNotificationAction
-interface DebugErrorAction : Debug, ErrorAction
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
index 1c984ff096..0a43a25887 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt
@@ -5,45 +5,14 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.StateDialog
-import com.tangem.tap.common.redux.DebugErrorAction
-import com.tangem.tap.common.redux.ErrorAction
-import com.tangem.tap.domain.TapError
-import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import org.rekotlin.Action
sealed class GlobalAction : Action {
- // notifications
- data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
- data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
-
// dialogs
data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
object HideDialog : GlobalAction()
- sealed class Onboarding : GlobalAction() {
- /**
- * Initiate an onboarding process.
- * For resuming unfinished backup of standard Wallet see
- * BackupAction.CheckForUnfinishedBackup, GlobalAction.Onboarding.StartForUnfinishedBackup
- */
- data class Start(
- val scanResponse: ScanResponse,
- val source: BackupStartedSource,
- val canSkipBackup: Boolean = true,
- ) : Onboarding()
-
- /**
- * Initiate resuming of unfinished backup for standard Wallet.
- * See more BackupAction.CheckForUnfinishedBackup
- */
- data class StartForUnfinishedBackup(val addedBackupCardsCount: Int) : Onboarding()
-
- object Stop : Onboarding()
-
- data class ShouldResetCardOnCreate(val shouldReset: Boolean) : Onboarding()
- }
-
object ScanFailsCounter {
data class ChooseBehavior(
val result: CompletionResult,
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
index c8372a3d0d..51da092058 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt
@@ -1,7 +1,6 @@
package com.tangem.tap.common.redux.global
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.features.onboarding.OnboardingManager
import com.tangem.utils.extensions.replaceBy
import org.rekotlin.Action
@@ -12,21 +11,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
val globalState = state.globalState
return when (action) {
- is GlobalAction.Onboarding.Start -> {
- val onboardingManager = OnboardingManager(action.scanResponse)
- globalState.copy(onboardingState = OnboardingState(true, onboardingManager))
- }
- is GlobalAction.Onboarding.StartForUnfinishedBackup -> {
- globalState.copy(onboardingState = OnboardingState(true, null))
- }
- is GlobalAction.Onboarding.Stop -> {
- globalState.copy(onboardingState = OnboardingState(false))
- }
- is GlobalAction.Onboarding.ShouldResetCardOnCreate -> {
- globalState.copy(
- onboardingState = globalState.onboardingState.copy(shouldResetOnCreate = action.shouldReset),
- )
- }
is GlobalAction.ScanFailsCounter.Increment -> {
globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1)
}
diff --git a/app/src/main/java/com/tangem/tap/common/redux/legacy/LegacyMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/legacy/LegacyMiddleware.kt
index 22932d20e9..1eea5ec1a0 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/legacy/LegacyMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/legacy/LegacyMiddleware.kt
@@ -5,10 +5,8 @@ import com.tangem.domain.redux.LegacyAction
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.details.redux.AppSettingsState
import com.tangem.tap.features.details.redux.DetailsAction
-import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
@@ -26,15 +24,6 @@ internal object LegacyMiddleware {
{ next ->
{ action ->
when (action) {
- is LegacyAction.StartOnboardingProcess -> {
- store.dispatch(
- GlobalAction.Onboarding.Start(
- scanResponse = action.scanResponse,
- source = BackupStartedSource.CreateBackup,
- canSkipBackup = action.canSkipBackup,
- ),
- )
- }
is LegacyAction.PrepareDetailsScreen -> {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt
deleted file mode 100644
index 3996e28d5c..0000000000
--- a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.tangem.tap.common.toggleWidget
-
-import android.view.View
-import android.view.ViewGroup
-import android.view.animation.*
-import android.widget.ViewSwitcher
-import com.tangem.tap.common.entities.ProgressState
-import com.tangem.wallet.R
-
-/**
-[REDACTED_AUTHOR]
- */
-@Suppress("MagicNumber")
-class RefreshBalanceWidget(root: ViewGroup) : ViewStateWidget {
-
- var isShowing: Boolean? = null
- private set
-
- override val mainView: View = root.findViewById(R.id.btn_refresh_balance)
-
- private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) }
-
- private val arrowView = mainView.findViewById(R.id.imv_arrow_refresh)
-
- private var progressViewAnimation: Animation? = null
-
- init {
- viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply {
- duration = 400
- interpolator = LinearInterpolator()
- }
- viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply {
- duration = 400
- interpolator = LinearInterpolator()
- }
- }
-
- override fun changeState(state: WidgetState) {
- val progressState = state as? ProgressState ?: return
-
- val currentStateByView = getState()
- when {
- currentStateByView == progressState -> return
- currentStateByView == ProgressState.Done -> if (progressState == ProgressState.Error) return
- }
-
- mainView.isClickable = currentStateByView == ProgressState.Done || currentStateByView == ProgressState.Error
- animateState(progressState)
- viewSwitcher.showNext()
- }
-
- @Suppress("MagicNumber")
- private fun animateState(state: ProgressState) {
- when (state) {
- ProgressState.Done, ProgressState.Error -> {
- progressViewAnimation?.cancel()
- progressViewAnimation = null
- }
- ProgressState.Loading -> {
- progressViewAnimation = RotateAnimation(
- /* fromDegrees = */
- 0f,
- /* toDegrees = */
- 360f,
- /* pivotXType = */
- Animation.RELATIVE_TO_SELF,
- /* pivotXValue = */
- 0.5f,
- /* pivotYType = */
- Animation.RELATIVE_TO_SELF,
- /* pivotYValue = */
- 0.5f,
- )
- progressViewAnimation?.duration = 700
- progressViewAnimation?.interpolator = AccelerateInterpolator()
- progressViewAnimation?.repeatCount = -1
- progressViewAnimation?.let { arrowView.startAnimation(it) }
- }
- else -> {}
- }
- }
-
- fun show(show: Boolean) {
- if (show == isShowing) return
-
- isShowing = show
- if (show) {
- mainView.startAnimation(ShowAnimation())
- } else {
- mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true })
- }
- }
-
- private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container
-
- private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading
-}
-
-@Suppress("MagicNumber")
-class ShowAnimation : AnimationSet(true) {
- init {
- addAnimation(
- ScaleAnimation(
- 0f,
- 1f,
- 0f,
- 1f,
- Animation.RELATIVE_TO_SELF,
- 0.5f,
- Animation.RELATIVE_TO_SELF,
- 0.5f,
- ),
- )
- addAnimation(AlphaAnimation(0f, 1f))
- duration = 300
- interpolator = AnticipateOvershootInterpolator()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt
deleted file mode 100644
index df54352380..0000000000
--- a/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.tangem.tap.common.ui
-
-import android.content.Context
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.widget.Toast
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.tangem.core.analytics.Analytics
-import com.tangem.tap.common.analytics.events.Token
-import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.AppDialog
-import com.tangem.tap.domain.model.WalletAddressData
-import com.tangem.tap.proxy.redux.DaggerGraphState
-import com.tangem.tap.store
-import com.tangem.wallet.R
-import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
-
-/**
-[REDACTED_AUTHOR]
- */
-internal class AddressInfoBottomSheetDialog(
- private val stateDialog: AppDialog.AddressInfoDialog,
- context: Context,
-) : BottomSheetDialog(context) {
-
- var binding: DialogOnboardingAddressInfoBinding? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- binding = DialogOnboardingAddressInfoBinding
- .inflate(LayoutInflater.from(context))
- setContentView(binding!!.root)
- behavior.state = BottomSheetBehavior.STATE_EXPANDED
- setOnCancelListener {
- store.dispatchDialogHide()
- binding = null
- }
- }
-
- override fun show() {
- super.show()
- Analytics.send(Token.Receive.ScreenOpened(stateDialog.currency.currencySymbol))
- showData(data = stateDialog.addressData)
- }
-
- private fun showData(data: WalletAddressData) = with(binding!!) {
- pseudoToolbar.imvClose.setOnClickListener {
- dismissWithAnimation = true
- cancel()
- }
- imvQrCode.setImageBitmap(data.shareUrl.toQrCode())
- tvAddress.text = data.address
- btnFlCopyAddress.setOnClickListener {
- Analytics.send(Token.Receive.ButtonCopyAddress())
-
- Toast
- .makeText(context, R.string.wallet_notification_address_copied, Toast.LENGTH_SHORT)
- .show()
-
- store.inject(DaggerGraphState::clipboardManager).setText(text = data.address, isSensitive = true)
- }
- btnFlShare.setOnClickListener {
- Analytics.send(Token.Receive.ButtonShareAddress())
- store.dispatchShare(data.shareUrl)
- }
- val blockchain = stateDialog.currency.blockchain
- tvReceiveMessage.text = tvReceiveMessage.getString(
- id = R.string.address_qr_code_message_format,
- blockchain.getCoinName(),
- blockchain.currency,
- blockchain.fullName,
- )
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/data/DefaultVisaAuthTokenStorage.kt b/app/src/main/java/com/tangem/tap/data/DefaultVisaAuthTokenStorage.kt
index a6128ddfe6..9859e3e4be 100644
--- a/app/src/main/java/com/tangem/tap/data/DefaultVisaAuthTokenStorage.kt
+++ b/app/src/main/java/com/tangem/tap/data/DefaultVisaAuthTokenStorage.kt
@@ -20,18 +20,22 @@ internal class DefaultVisaAuthTokenStorage @Inject constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
) : VisaAuthTokenStorage {
- private val secureStorage = AndroidSecureStorage(
- preferences = SecureStorage.createEncryptedSharedPreferences(
- context = applicationContext,
- storageName = "visa_auth_storage",
- ),
- )
+ private val secureStorage by lazy {
+ AndroidSecureStorage(
+ preferences = SecureStorage.createEncryptedSharedPreferences(
+ context = applicationContext,
+ storageName = "visa_auth_storage",
+ ),
+ )
+ }
- private val moshi = Moshi.Builder()
- .add(KotlinJsonAdapterFactory())
- .build()
+ private val moshi by lazy {
+ Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+ }
- private val tokensAdapter = moshi.adapter(VisaAuthTokens::class.java)
+ private val tokensAdapter by lazy { moshi.adapter(VisaAuthTokens::class.java) }
override suspend fun store(cardId: String, tokens: VisaAuthTokens) = withContext(dispatcherProvider.io) {
val json = tokensAdapter.toJson(tokens)
diff --git a/app/src/main/java/com/tangem/tap/data/DefaultVisaOTPStorage.kt b/app/src/main/java/com/tangem/tap/data/DefaultVisaOTPStorage.kt
index f752d10dea..b87d3fbfdf 100644
--- a/app/src/main/java/com/tangem/tap/data/DefaultVisaOTPStorage.kt
+++ b/app/src/main/java/com/tangem/tap/data/DefaultVisaOTPStorage.kt
@@ -20,12 +20,14 @@ class DefaultVisaOTPStorage @Inject constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
) : VisaOTPStorage {
- private val secureStorage = AndroidSecureStorage(
- preferences = SecureStorage.createEncryptedSharedPreferences(
- context = applicationContext,
- storageName = "visa_otp_storage",
- ),
- )
+ private val secureStorage by lazy {
+ AndroidSecureStorage(
+ preferences = SecureStorage.createEncryptedSharedPreferences(
+ context = applicationContext,
+ storageName = "visa_otp_storage",
+ ),
+ )
+ }
override suspend fun saveOTP(cardId: String, data: VisaOtpData) = withContext(dispatcherProvider.io) {
secureStorage.store(data.rootOTP, VISA_ROOT_OTP_KEY_PREFIX + cardId)
diff --git a/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt
new file mode 100644
index 0000000000..02a9b8be72
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/data/FirebasePushNotificationsTokenProvider.kt
@@ -0,0 +1,11 @@
+package com.tangem.tap.data
+
+import com.google.firebase.messaging.FirebaseMessaging
+import com.tangem.utils.notifications.PushNotificationsTokenProvider
+import kotlinx.coroutines.tasks.await
+
+internal class FirebasePushNotificationsTokenProvider : PushNotificationsTokenProvider {
+ override suspend fun getToken(): String {
+ return FirebaseMessaging.getInstance().token.await()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt
index 9870501655..ae906c4a76 100644
--- a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt
+++ b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt
@@ -24,7 +24,7 @@ internal class RuntimeUserWalletsStore(
return userWalletsListManager.userWalletsSync.firstOrNull { it.walletId == key }
}
- override suspend fun getSyncStrict(key: UserWalletId): UserWallet {
+ override fun getSyncStrict(key: UserWalletId): UserWallet {
return requireNotNull(getSyncOrNull(key)) { "Unable to find user wallet with provided ID: $key" }
}
diff --git a/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt b/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt
new file mode 100644
index 0000000000..063efaf3dc
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/di/data/PushNotificationsModule.kt
@@ -0,0 +1,18 @@
+package com.tangem.tap.di.data
+
+import com.tangem.tap.data.FirebasePushNotificationsTokenProvider
+import com.tangem.utils.notifications.PushNotificationsTokenProvider
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface PushNotificationsModule {
+
+ @Binds
+ @Singleton
+ fun bindPushNotificationsTokenProvider(impl: FirebasePushNotificationsTokenProvider): PushNotificationsTokenProvider
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt
index f6467cc073..b55ef9a8f7 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt
@@ -5,6 +5,8 @@ import com.tangem.domain.managetokens.*
import com.tangem.domain.managetokens.repository.CustomTokensRepository
import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
+import com.tangem.domain.quotes.multi.MultiQuoteFetcher
+import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
@@ -73,6 +75,8 @@ internal object ManageTokensDomainModule {
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): SaveManagedTokensUseCase {
return SaveManagedTokensUseCase(
@@ -84,6 +88,8 @@ internal object ManageTokensDomainModule {
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt
index 3a568db5e6..7666729a36 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt
@@ -5,6 +5,9 @@ import com.tangem.domain.card.repository.DerivationsRepository
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
+import com.tangem.domain.quotes.multi.MultiQuoteFetcher
+import com.tangem.domain.quotes.single.SingleQuoteSupplier
+import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.TokensFeatureToggles
import com.tangem.domain.tokens.repository.CurrenciesRepository
@@ -49,8 +52,16 @@ object MarketsDomainModule {
@Provides
@Singleton
- fun provideGetTokenQuotesUseCase(quotesRepository: QuotesRepository): GetCurrencyQuotesUseCase {
- return GetCurrencyQuotesUseCase(quotesRepository = quotesRepository)
+ fun provideGetTokenQuotesUseCase(
+ quotesRepository: QuotesRepository,
+ singleQuoteSupplier: SingleQuoteSupplier,
+ tokensFeatureToggles: TokensFeatureToggles,
+ ): GetCurrencyQuotesUseCase {
+ return GetCurrencyQuotesUseCase(
+ quotesRepository = quotesRepository,
+ singleQuoteSupplier = singleQuoteSupplier,
+ tokensFeatureToggles = tokensFeatureToggles,
+ )
}
@Provides
@@ -63,6 +74,8 @@ object MarketsDomainModule {
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): SaveMarketTokensUseCase {
return SaveMarketTokensUseCase(
@@ -73,6 +86,8 @@ object MarketsDomainModule {
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
diff --git a/app/src/main/java/com/tangem/tap/di/domain/MnemonicModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MnemonicModule.kt
deleted file mode 100644
index 8f62a7e255..0000000000
--- a/app/src/main/java/com/tangem/tap/di/domain/MnemonicModule.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.tangem.tap.di.domain
-
-import android.content.Context
-import com.tangem.crypto.bip39.Wordlist
-import com.tangem.feature.onboarding.data.DefaultMnemonicRepository
-import com.tangem.feature.onboarding.data.MnemonicRepository
-import com.tangem.sdk.extensions.getWordlist
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
-import dagger.hilt.components.SingletonComponent
-import javax.inject.Singleton
-
-@Module
-@InstallIn(SingletonComponent::class)
-class MnemonicModule {
-
- @Provides
- @Singleton
- fun provideMnemonicRepository(@ApplicationContext context: Context): MnemonicRepository {
- return DefaultMnemonicRepository(Wordlist.getWordlist(context))
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt
index a8cb6b030d..090a030029 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt
@@ -35,6 +35,16 @@ internal object NFTDomainModule {
nftRepository = nftRepository,
)
+ @Provides
+ @Singleton
+ fun providesRefreshAllNFTUseCase(
+ currenciesRepository: CurrenciesRepository,
+ nftRepository: NFTRepository,
+ ): RefreshAllNFTUseCase = RefreshAllNFTUseCase(
+ currenciesRepository = currenciesRepository,
+ nftRepository = nftRepository,
+ )
+
@Provides
@Singleton
fun providesFetchNFTCollectionAssetsUseCase(nftRepository: NFTRepository): FetchNFTCollectionAssetsUseCase =
@@ -47,7 +57,7 @@ internal object NFTDomainModule {
fun providesGetNFTAvailableNetworksUseCase(
nftRepository: NFTRepository,
currenciesRepository: CurrenciesRepository,
- ): GetNFTAvailableNetworksUseCase = GetNFTAvailableNetworksUseCase(
+ ): GetNFTNetworksUseCase = GetNFTNetworksUseCase(
currenciesRepository = currenciesRepository,
nftRepository = nftRepository,
)
@@ -66,4 +76,11 @@ internal object NFTDomainModule {
GetNFTNetworkStatusUseCase(
networksRepository = networksRepository,
)
+
+ @Provides
+ @Singleton
+ fun providesGetNFTExploreUrlUseCase(nftRepository: NFTRepository): GetNFTExploreUrlUseCase =
+ GetNFTExploreUrlUseCase(
+ nftRepository = nftRepository,
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt
new file mode 100644
index 0000000000..fe5ea6d52f
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/di/domain/NotificationsDomainModule.kt
@@ -0,0 +1,35 @@
+package com.tangem.tap.di.domain
+
+import com.tangem.domain.notifications.GetApplicationIdUseCase
+import com.tangem.domain.notifications.SendPushTokenUseCase
+import com.tangem.domain.notifications.repository.NotificationsRepository
+import com.tangem.utils.notifications.PushNotificationsTokenProvider
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal object NotificationsDomainModule {
+
+ @Provides
+ @Singleton
+ fun providesGetApplicationIdUseCase(notificationsRepository: NotificationsRepository): GetApplicationIdUseCase =
+ GetApplicationIdUseCase(
+ notificationsRepository = notificationsRepository,
+ )
+
+ @Provides
+ @Singleton
+ fun providesSendPushTokenUseCase(
+ notificationsRepository: NotificationsRepository,
+ getApplicationIdUseCase: GetApplicationIdUseCase,
+ pushNotificationsTokenProvider: PushNotificationsTokenProvider,
+ ): SendPushTokenUseCase = SendPushTokenUseCase(
+ notificationsRepository = notificationsRepository,
+ getApplicationIdUseCase = getApplicationIdUseCase,
+ pushNotificationsTokenProvider = pushNotificationsTokenProvider,
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt
index cb6976b9b8..22f61dc1f1 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/StakingDomainModule.kt
@@ -1,7 +1,11 @@
package com.tangem.tap.di.domain
import com.tangem.domain.staking.*
-import com.tangem.domain.staking.repositories.*
+import com.tangem.domain.staking.repositories.StakingActionRepository
+import com.tangem.domain.staking.repositories.StakingErrorResolver
+import com.tangem.domain.staking.repositories.StakingRepository
+import com.tangem.domain.staking.repositories.StakingTransactionHashRepository
+import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
import com.tangem.domain.walletmanager.WalletManagersFacade
import dagger.Module
import dagger.Provides
@@ -92,10 +96,12 @@ internal object StakingDomainModule {
fun provideFetchStakingYieldBalanceUseCase(
stakingRepository: StakingRepository,
stakingErrorResolver: StakingErrorResolver,
+ singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
): FetchStakingYieldBalanceUseCase {
return FetchStakingYieldBalanceUseCase(
stakingRepository = stakingRepository,
stakingErrorResolver = stakingErrorResolver,
+ singleYieldBalanceFetcher = singleYieldBalanceFetcher,
)
}
diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
index 237b83807e..ff23a2c9e2 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
@@ -7,7 +7,13 @@ import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.promo.PromoRepository
+import com.tangem.domain.quotes.QuotesRepositoryV2
+import com.tangem.domain.quotes.multi.MultiQuoteFetcher
+import com.tangem.domain.quotes.single.SingleQuoteSupplier
+import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
import com.tangem.domain.staking.repositories.StakingRepository
+import com.tangem.domain.staking.single.SingleYieldBalanceFetcher
+import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
@@ -35,6 +41,8 @@ internal object TokensDomainModule {
stakingRepository: StakingRepository,
quotesRepository: QuotesRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): AddCryptoCurrenciesUseCase {
return AddCryptoCurrenciesUseCase(
@@ -43,6 +51,8 @@ internal object TokensDomainModule {
stakingRepository = stakingRepository,
quotesRepository = quotesRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ singleYieldBalanceFetcher = singleYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -55,6 +65,8 @@ internal object TokensDomainModule {
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): FetchTokenListUseCase {
return FetchTokenListUseCase(
@@ -63,6 +75,8 @@ internal object TokensDomainModule {
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -168,6 +182,8 @@ internal object TokensDomainModule {
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ singleYieldBalanceFetcher: SingleYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): FetchCurrencyStatusUseCase {
return FetchCurrencyStatusUseCase(
@@ -176,6 +192,8 @@ internal object TokensDomainModule {
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ singleYieldBalanceFetcher = singleYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -188,6 +206,8 @@ internal object TokensDomainModule {
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
tokensFeatureToggles: TokensFeatureToggles,
): FetchCardTokenListUseCase {
return FetchCardTokenListUseCase(
@@ -196,6 +216,8 @@ internal object TokensDomainModule {
quotesRepository = quotesRepository,
stakingRepository = stakingRepository,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -364,10 +386,14 @@ internal object TokensDomainModule {
fun provideRefreshMultiCurrencyWalletQuotesUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ tokensFeatureToggles: TokensFeatureToggles,
): RefreshMultiCurrencyWalletQuotesUseCase {
return RefreshMultiCurrencyWalletQuotesUseCase(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
+ multiQuoteFetcher = multiQuoteFetcher,
+ tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -386,20 +412,30 @@ internal object TokensDomainModule {
tokensFeatureToggles: TokensFeatureToggles,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
+ quotesRepositoryV2: QuotesRepositoryV2,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ singleQuoteSupplier: SingleQuoteSupplier,
+ singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
): BaseCurrenciesStatusesOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
+ quotesRepositoryV2 = quotesRepositoryV2,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ singleQuoteSupplier = singleQuoteSupplier,
+ singleYieldBalanceSupplier = singleYieldBalanceSupplier,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
@@ -410,21 +446,37 @@ internal object TokensDomainModule {
tokensFeatureToggles: TokensFeatureToggles,
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
+ quotesRepositoryV2: QuotesRepositoryV2,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
+ multiQuoteFetcher: MultiQuoteFetcher,
+ singleQuoteSupplier: SingleQuoteSupplier,
+ singleYieldBalanceSupplier: SingleYieldBalanceSupplier,
+ multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
): BaseCurrencyStatusOperations {
return CachedCurrenciesStatusesOperations(
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
+ quotesRepositoryV2 = quotesRepositoryV2,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
+ multiQuoteFetcher = multiQuoteFetcher,
+ singleQuoteSupplier = singleQuoteSupplier,
+ singleYieldBalanceSupplier = singleYieldBalanceSupplier,
+ multiYieldBalanceFetcher = multiYieldBalanceFetcher,
tokensFeatureToggles = tokensFeatureToggles,
)
}
+
+ @Provides
+ @Singleton
+ fun provideGetCryptoCurrenciesUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrenciesUseCase {
+ return GetCryptoCurrenciesUseCase(currenciesRepository)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
index adc63cbd3d..77c2518104 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
@@ -30,6 +30,21 @@ internal object TransactionDomainModule {
)
}
+ @Provides
+ @Singleton
+ fun provideGetEthSpecificFeeUseCase(walletManagersFacade: WalletManagersFacade): GetEthSpecificFeeUseCase {
+ return GetEthSpecificFeeUseCase(walletManagersFacade = walletManagersFacade)
+ }
+
+ @Provides
+ @Singleton
+ fun provideTransferGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetTransferFeeUseCase {
+ return GetTransferFeeUseCase(
+ walletManagersFacade = walletManagersFacade,
+ demoConfig = DemoConfig(),
+ )
+ }
+
@Provides
@Singleton
fun provideSendTransactionUseCase(
@@ -147,4 +162,38 @@ internal object TransactionDomainModule {
fun provideGetAllowanceUseCase(transactionRepository: TransactionRepository): GetAllowanceUseCase {
return GetAllowanceUseCase(transactionRepository)
}
+
+ @Provides
+ @Singleton
+ fun provideCreateTransferTransactionUseCase(
+ transactionRepository: TransactionRepository,
+ ): CreateTransferTransactionUseCase {
+ return CreateTransferTransactionUseCase(transactionRepository)
+ }
+
+ @Provides
+ @Singleton
+ fun providePrepareForSendUseCase(
+ transactionRepository: TransactionRepository,
+ cardSdkConfigRepository: CardSdkConfigRepository,
+ ): PrepareForSendUseCase {
+ return PrepareForSendUseCase(transactionRepository, cardSdkConfigRepository)
+ }
+
+ @Provides
+ @Singleton
+ fun provideSignUseCase(
+ walletManagersFacade: WalletManagersFacade,
+ cardSdkConfigRepository: CardSdkConfigRepository,
+ ): SignUseCase {
+ return SignUseCase(cardSdkConfigRepository, walletManagersFacade)
+ }
+
+ @Provides
+ @Singleton
+ fun provideCreateNFTTransferTransactionUseCase(
+ transactionRepository: TransactionRepository,
+ ): CreateNFTTransferTransactionUseCase {
+ return CreateNFTTransferTransactionUseCase(transactionRepository)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletConnectDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletConnectDomainModule.kt
index e542ddc5fd..fd5d2491d3 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/WalletConnectDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/WalletConnectDomainModule.kt
@@ -2,6 +2,8 @@ package com.tangem.tap.di.domain
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
import com.tangem.domain.walletconnect.repository.WalletConnectRepository
+import com.tangem.domain.walletconnect.repository.WcSessionsManager
+import com.tangem.domain.walletconnect.usecase.WcSessionsUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -19,4 +21,10 @@ internal object WalletConnectDomainModule {
): CheckIsWalletConnectAvailableUseCase {
return CheckIsWalletConnectAvailableUseCase(walletConnectRepository = walletConnectRepository)
}
+
+ @Provides
+ @Singleton
+ fun providesWcSessionsUseCase(sessionsManager: WcSessionsManager): WcSessionsUseCase {
+ return WcSessionsUseCase(sessionsManager)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
index 9be71f685e..4096521b75 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
@@ -14,6 +14,8 @@ import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSy
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.features.nft.NFTFeatureToggles
+import com.tangem.operations.attestation.CardArtworksProvider
+import com.tangem.sdk.api.featuretoggles.CardSdkFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
@@ -172,8 +174,16 @@ internal object WalletsDomainModule {
@Provides
@Singleton
- fun provideGetCardImageUseCase(onlineCardVerifier: OnlineCardVerifier): GetCardImageUseCase {
- return GetCardImageUseCase(verifier = onlineCardVerifier)
+ fun provideGetCardImageUseCase(
+ onlineCardVerifier: OnlineCardVerifier,
+ cardArtworksProvider: CardArtworksProvider,
+ cardSdkFeatureToggles: CardSdkFeatureToggles,
+ ): GetCardImageUseCase {
+ return GetCardImageUseCase(
+ verifier = onlineCardVerifier,
+ cardArtworksProvider = cardArtworksProvider,
+ cardSdkFeatureToggles = cardSdkFeatureToggles,
+ )
}
@Provides
diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
index 8225f9014c..63058e7738 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
@@ -7,7 +7,6 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
-import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
@@ -42,7 +41,6 @@ class TapWalletManager(
withMainContext {
// Order is important
- store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
index 7117819925..547dfbd6ce 100644
--- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
+++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt
@@ -6,6 +6,7 @@ import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.Artwork
+import com.tangem.operations.attestation.CardArtworksProvider
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
@@ -37,7 +38,7 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(
if (artworkId.isNullOrEmpty()) {
ifAnyError()
} else {
- OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
+ CardArtworksProvider.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
index 6ea8247faf..29665865af 100644
--- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
+++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
@@ -21,7 +21,6 @@ import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
-import com.tangem.domain.common.TapWorkarounds.canSkipBackup
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.feedback.models.FeedbackEmailType
@@ -34,9 +33,6 @@ import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.disclaimer.createDisclaimer
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.TwinCardsStep
-import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.mainScope
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
@@ -208,8 +204,6 @@ internal class LegacyScanProcessor @Inject constructor(
crossinline onCancel: suspend () -> Unit,
crossinline onSuccess: suspend (ScanResponse) -> Unit,
) {
- store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(scanResponse))
-
checkCardWasUsedInApp(
scanResponse = scanResponse,
onCancel = {
@@ -222,38 +216,26 @@ internal class LegacyScanProcessor @Inject constructor(
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
Analytics.addContext(scanResponse)
onWalletNotCreated()
- // must check skip backup using card canSkipBackup
- store.dispatchOnMain(
- GlobalAction.Onboarding.Start(
+ navigateTo(
+ AppRoute.Onboarding(
scanResponse = scanResponse,
- source = BackupStartedSource.Onboarding,
- canSkipBackup = scanResponse.card.canSkipBackup,
+ mode = AppRoute.Onboarding.Mode.Onboarding,
),
- )
- val appScreen = OnboardingHelper.whereToNavigate(scanResponse)
- navigateTo(appScreen) { onProgressStateChange(it) }
+ ) { onProgressStateChange(it) }
} else {
Analytics.setContext(scanResponse)
- val isTwinRefactoringEnabled =
- store.inject(DaggerGraphState::onboardingV2FeatureToggles).isTwinRefactoringEnabled
-
val wasTwinsOnboardingShown =
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()
if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) {
onWalletNotCreated()
- if (isTwinRefactoringEnabled) {
- navigateTo(
- AppRoute.Onboarding(
- scanResponse = scanResponse,
- mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
- ),
- ) { onProgressStateChange(it) }
- } else {
- store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse)))
- navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) }
- }
+ navigateTo(
+ AppRoute.Onboarding(
+ scanResponse = scanResponse,
+ mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
+ ),
+ ) { onProgressStateChange(it) }
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
onSuccess(scanResponse)
diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt
index b9d6aff5fd..0912058b65 100644
--- a/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt
+++ b/app/src/main/java/com/tangem/tap/domain/scanCard/chains/CheckForOnboardingChain.kt
@@ -5,21 +5,15 @@ import arrow.core.right
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.domain.card.ScanCardException
-import com.tangem.domain.common.TapWorkarounds.canSkipBackup
import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.core.chain.ResultChain
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.addContext
-import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.AppState
-import com.tangem.tap.common.redux.global.GlobalAction
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.TwinCardsStep
-import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE
import kotlinx.coroutines.delay
@@ -40,46 +34,30 @@ class CheckForOnboardingChain(
) : ResultChain() {
override suspend fun launch(previousChainResult: ScanResponse): ScanChainResult {
- store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(previousChainResult))
-
return when {
OnboardingHelper.isOnboardingCase(previousChainResult) -> {
Analytics.addContext(previousChainResult)
- // must check skip backup using card canSkipBackup
- store.dispatchOnMain(
- GlobalAction.Onboarding.Start(
+ ScanChainException.OnboardingNeeded(
+ AppRoute.Onboarding(
scanResponse = previousChainResult,
- source = BackupStartedSource.Onboarding,
- canSkipBackup = previousChainResult.card.canSkipBackup,
+ mode = AppRoute.Onboarding.Mode.Onboarding,
),
- )
- val route = OnboardingHelper.whereToNavigate(previousChainResult)
- ScanChainException.OnboardingNeeded(route).left()
+ ).left()
}
else -> {
Analytics.setContext(previousChainResult)
- val isTwinRefactoringEnabled =
- store.inject(DaggerGraphState::onboardingV2FeatureToggles).isTwinRefactoringEnabled
-
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
.invokeSync()
// If twins was twinned previously but twins welcome not shown
if (previousChainResult.twinsIsTwinned() && !wasTwinsOnboardingShown) {
- if (isTwinRefactoringEnabled) {
- ScanChainException.OnboardingNeeded(
- AppRoute.Onboarding(
- scanResponse = previousChainResult,
- mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
- ),
- ).left()
- } else {
- store.dispatchOnMain(
- TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)),
- )
- ScanChainException.OnboardingNeeded(AppRoute.OnboardingTwins).left()
- }
+ ScanChainException.OnboardingNeeded(
+ AppRoute.Onboarding(
+ scanResponse = previousChainResult,
+ mode = AppRoute.Onboarding.Mode.WelcomeOnlyTwin,
+ ),
+ ).left()
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
previousChainResult.right()
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt
index 890ce4ba9b..dd4e607371 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCardActivationTask.kt
@@ -4,14 +4,12 @@ import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.raise.catch
import arrow.core.raise.either
+import com.tangem.blockchain.common.UnmarshalHelper
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
-import com.tangem.common.core.CardSession
-import com.tangem.common.core.CardSessionRunnable
-import com.tangem.common.core.CompletionCallback
-import com.tangem.common.core.TangemError
-import com.tangem.common.core.TangemSdkError
+import com.tangem.common.core.*
import com.tangem.common.extensions.hexToBytes
+import com.tangem.common.extensions.toDecompressedPublicKey
import com.tangem.common.extensions.toHexString
import com.tangem.common.map
import com.tangem.common.timemeasure.RealtimeMonotonicTimeSource
@@ -46,7 +44,7 @@ import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.time.measureTimedValue
-@Suppress("LongParameterList")
+@Suppress("LongParameterList", "LargeClass")
class VisaCardActivationTask @AssistedInject constructor(
@Assisted private val mode: VisaCardActivationTaskMode,
@Assisted private val activationInput: VisaActivationInput,
@@ -97,7 +95,23 @@ class VisaCardActivationTask @AssistedInject constructor(
context.signAuthorizationChallenge(mode.authorizationChallenge)
}
is VisaCardActivationTaskMode.SignOnly -> {
- context.signData(mode.dataToSignByCardWallet)
+ val wallet =
+ card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
+ ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
+
+ val derivedPublicKey = when (val deriveKeyResult = context.deriveKey(wallet.publicKey)) {
+ is CompletionResult.Failure -> {
+ return CompletionResult.Failure(deriveKeyResult.error)
+ }
+ is CompletionResult.Success -> {
+ deriveKeyResult.data
+ }
+ }
+
+ context.signData(
+ mode.dataToSignByCardWallet,
+ derivedPublicKey,
+ )
}
}
}
@@ -138,9 +152,35 @@ class VisaCardActivationTask @AssistedInject constructor(
private suspend fun SessionContext.processSignedAuthorizationChallenge(
signedChallenge: VisaAuthSignedChallenge,
): CompletionResult {
+ when (val createWalletResult = createWallet()) {
+ is CompletionResult.Failure -> return CompletionResult.Failure(createWalletResult.error)
+ is CompletionResult.Success -> {}
+ }
+
+ val card =
+ session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
+
+ val wallet =
+ card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
+ ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
+
+ val derivedPublicKey = when (val deriveKeyResult = deriveKey(wallet.publicKey)) {
+ is CompletionResult.Failure -> {
+ return CompletionResult.Failure(deriveKeyResult.error)
+ }
+ is CompletionResult.Success -> {
+ deriveKeyResult.data
+ }
+ }
+
+ val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(derivedPublicKey.publicKey)
+ .getOrElse { return CompletionResult.Failure(it.tangemError) }
+ .value
+
return coroutineScope {
- val dataToSignDeferred = async { getDataToSign(signedChallenge) }
- val otpTaskDeferred = async { createWallet() }
+ val dataToSignDeferred =
+ async { getDataToSign(signedChallenge = signedChallenge, cardWalletAddress = walletAddress) }
+ val otpTaskDeferred = async { createOTP() }
val dataToSign = dataToSignDeferred.await()
.getOrElse {
@@ -150,12 +190,16 @@ class VisaCardActivationTask @AssistedInject constructor(
otpTaskDeferred.await()
- signData(dataToSign)
+ signData(
+ dataToSign = dataToSign,
+ derivedPublicKey = derivedPublicKey,
+ )
}
}
private suspend fun SessionContext.getDataToSign(
signedChallenge: VisaAuthSignedChallenge,
+ cardWalletAddress: String,
): Either = either {
catch(
block = {
@@ -168,7 +212,12 @@ class VisaCardActivationTask @AssistedInject constructor(
raise(VisaActivationError.WrongRemoteState.tangemError)
}
- visaActivationRepository.getCardWalletAcceptanceData(remoteState.request)
+ visaActivationRepository.getCardWalletAcceptanceData(
+ VisaCardWalletDataToSignRequest(
+ activationOrderInfo = remoteState.activationOrderInfo,
+ cardWalletAddress = cardWalletAddress,
+ ),
+ )
},
catch = {
raise(VisaAuthorizationAPIError.tangemError)
@@ -182,7 +231,7 @@ class VisaCardActivationTask @AssistedInject constructor(
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
return if (card.wallets.any { it.curve == EllipticCurve.Secp256k1 }) {
- createOTP()
+ CompletionResult.Success(Unit)
} else {
val createWalletTask = CreateWalletTask(EllipticCurve.Secp256k1)
@@ -199,7 +248,7 @@ class VisaCardActivationTask @AssistedInject constructor(
when (val result = timedResult.value) {
is CompletionResult.Success -> {
Timber.i("CreateWalletTask success")
- createOTP()
+ CompletionResult.Success(Unit)
}
is CompletionResult.Failure -> {
Timber.e("CreateWalletTask failure ${result.error}")
@@ -245,26 +294,15 @@ class VisaCardActivationTask @AssistedInject constructor(
private suspend fun SessionContext.signData(
dataToSign: VisaDataToSignByCardWallet,
+ derivedPublicKey: ExtendedPublicKey,
): CompletionResult {
val card =
session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
+
val wallet =
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
- val derivedPublicKey = when (val deriveKeyResult = deriveKey(wallet.publicKey)) {
- is CompletionResult.Failure -> {
- return CompletionResult.Failure(deriveKeyResult.error)
- }
- is CompletionResult.Success -> {
- deriveKeyResult.data
- }
- }
-
- val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(derivedPublicKey.publicKey)
- .getOrElse { return CompletionResult.Failure(it.tangemError) }
- .value
-
val task = SignHashCommand(
hash = dataToSign.hashToSign.hexToBytes(),
walletPublicKey = wallet.publicKey,
@@ -286,8 +324,8 @@ class VisaCardActivationTask @AssistedInject constructor(
Timber.i("SignHashCommand success")
handleSignedData(
dataToSign = dataToSign,
- walletAddress = walletAddress,
response = result.data,
+ derivedPublicKey = derivedPublicKey,
)
}
is CompletionResult.Failure -> {
@@ -313,7 +351,7 @@ class VisaCardActivationTask @AssistedInject constructor(
private suspend fun SessionContext.handleSignedData(
dataToSign: VisaDataToSignByCardWallet,
- walletAddress: String,
+ derivedPublicKey: ExtendedPublicKey,
response: SignHashResponse,
): CompletionResult {
val otp = otpStorage.getOTP(cardId) ?: run {
@@ -321,11 +359,16 @@ class VisaCardActivationTask @AssistedInject constructor(
otpStorage.getOTP(cardId) ?: return CompletionResult.Failure(VisaActivationError.MissingRootOTP.tangemError)
}
+ val rsvSignature = UnmarshalHelper.unmarshalSignatureExtended(
+ signature = response.signature,
+ hash = dataToSign.hashToSign.hexToBytes(),
+ publicKey = derivedPublicKey.publicKey.toDecompressedPublicKey(),
+ ).asRSVLegacyEVM().toHexString()
+
val signedActivationData = dataToSign.sign(
- cardWalletAddress = walletAddress,
rootOTP = otp.rootOTP.toHexString(),
otpCounter = otp.counter,
- signature = response.signature.toHexString(),
+ signature = rsvSignature,
)
return setupAccessCode().map {
diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt
index 18ec47b7fb..55dd835a3e 100644
--- a/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tasks/visa/VisaCustomerWalletApproveTask.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.domain.tasks.visa
import arrow.core.getOrElse
+import com.tangem.blockchain.common.UnmarshalHelper
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.common.card.CardWallet
@@ -10,6 +11,7 @@ import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.CompletionCallback
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
+import com.tangem.common.extensions.toDecompressedPublicKey
import com.tangem.common.extensions.toHexString
import com.tangem.core.error.ext.tangemError
import com.tangem.crypto.hdWallet.DerivationPath
@@ -129,6 +131,7 @@ class VisaCustomerWalletApproveTask(
targetWalletPublicKey = wallet.publicKey,
derivationPath = derivationPath,
session = session,
+ extendedPublicKey = extendedPublicKey,
callback = callback,
)
}
@@ -149,6 +152,7 @@ class VisaCustomerWalletApproveTask(
signApproveData(
targetWalletPublicKey = publicKey,
derivationPath = null,
+ extendedPublicKey = null,
session = session,
callback = callback,
)
@@ -157,11 +161,14 @@ class VisaCustomerWalletApproveTask(
private fun signApproveData(
targetWalletPublicKey: ByteArray,
derivationPath: DerivationPath?,
+ extendedPublicKey: ExtendedPublicKey?,
session: CardSession,
callback: CompletionCallback,
) {
+ val hashToSign = visaDataForApprove.dataToSign.hashToSign.hexToBytes()
+
val signTask = SignHashCommand(
- hash = visaDataForApprove.dataToSign.hashToSign.hexToBytes(),
+ hash = hashToSign,
walletPublicKey = targetWalletPublicKey,
derivationPath = derivationPath,
)
@@ -169,11 +176,18 @@ class VisaCustomerWalletApproveTask(
signTask.run(session) { result ->
when (result) {
is CompletionResult.Success -> {
+ val rsvSignature = UnmarshalHelper.unmarshalSignatureExtended(
+ signature = result.data.signature,
+ hash = hashToSign,
+ publicKey = extendedPublicKey?.publicKey?.toDecompressedPublicKey()
+ ?: targetWalletPublicKey.toDecompressedPublicKey(),
+ ).asRSVLegacyEVM().toHexString()
+
scanCard(
session = session,
callback = callback,
signedData = visaDataForApprove.dataToSign.sign(
- signature = result.data.signature.toHexString(),
+ signature = rsvSignature,
customerWalletAddress = visaDataForApprove.targetAddress,
),
)
diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/DefaultTokensFeatureToggles.kt b/app/src/main/java/com/tangem/tap/domain/tokens/DefaultTokensFeatureToggles.kt
index 2927924f8c..adc8e80741 100644
--- a/app/src/main/java/com/tangem/tap/domain/tokens/DefaultTokensFeatureToggles.kt
+++ b/app/src/main/java/com/tangem/tap/domain/tokens/DefaultTokensFeatureToggles.kt
@@ -9,4 +9,10 @@ internal class DefaultTokensFeatureToggles(
override val isNetworksLoadingRefactoringEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(name = "NETWORKS_LOADING_REFACTORING_ENABLED")
+
+ override val isQuotesLoadingRefactoringEnabled: Boolean
+ get() = featureTogglesManager.isFeatureEnabled(name = "QUOTES_LOADING_REFACTORING_ENABLED")
+
+ override val isStakingLoadingRefactoringEnabled: Boolean
+ get() = featureTogglesManager.isFeatureEnabled(name = "STAKING_LOADING_REFACTORING_ENABLED")
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt
index e7a9f1873f..95e75741b7 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletInformation.kt
@@ -21,8 +21,6 @@ internal data class UserWalletPublicInformation(
val name: String,
@Json(name = "walletId")
val walletId: UserWalletId,
- @Json(name = "artworkUrl")
- val artworkUrl: String,
@Json(name = "cardsInWallet")
val cardsInWallet: Set,
@Json(name = "scanResponse")
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt
index 9ee2498fa8..d963dc5794 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt
@@ -20,9 +20,12 @@ internal class DefaultUserWalletsPublicInformationRepository(
moshi: Moshi,
private val secureStorage: SecureStorage,
) : UserWalletsPublicInformationRepository {
- private val publicInformationAdapter: JsonAdapter> = moshi.adapter(
- Types.newParameterizedType(List::class.java, UserWalletPublicInformation::class.java),
- )
+
+ private val publicInformationAdapter: JsonAdapter> by lazy {
+ moshi.adapter(
+ Types.newParameterizedType(List::class.java, UserWalletPublicInformation::class.java),
+ )
+ }
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult {
return withContext(Dispatchers.IO) {
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt
index 13c56f4f22..a1888ea494 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt
@@ -24,12 +24,15 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
private val secureStorage: SecureStorage,
) : UserWalletsSensitiveInformationRepository {
- private val sensitiveInformationAdapter: JsonAdapter = moshi.adapter(
- UserWalletSensitiveInformation::class.java,
- )
- private val encryptedSensitiveInformationMapAdapter: JsonAdapter