Updated on 2026-08-14
This commit is contained in:
commit
4b31a0a471
937 changed files with 29472 additions and 3861 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import java.util.Properties
|
||||
import com.tangem.plugin.configuration.configurations.extension.kaptForObfuscatingVariants
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
alias(deps.plugins.android.application)
|
||||
|
|
@ -100,6 +100,7 @@ dependencies {
|
|||
implementation(projects.domain.qrScanning.models)
|
||||
implementation(projects.domain.staking)
|
||||
implementation(projects.domain.walletConnect)
|
||||
implementation(projects.domain.walletConnect.models)
|
||||
implementation(projects.domain.markets)
|
||||
implementation(projects.domain.manageTokens)
|
||||
implementation(projects.domain.nft)
|
||||
|
|
@ -107,6 +108,7 @@ dependencies {
|
|||
implementation(projects.domain.onramp)
|
||||
implementation(projects.domain.promo)
|
||||
implementation(projects.domain.promo.models)
|
||||
implementation(projects.domain.networks)
|
||||
|
||||
implementation(projects.common)
|
||||
implementation(projects.common.routing)
|
||||
|
|
@ -121,6 +123,7 @@ dependencies {
|
|||
implementation(projects.core.utils)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.core.error.ext)
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.libs.auth)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
|
@ -148,6 +151,7 @@ dependencies {
|
|||
implementation(projects.data.manageTokens)
|
||||
implementation(projects.data.nft)
|
||||
implementation(projects.data.onramp)
|
||||
implementation(projects.data.networks)
|
||||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
@ -169,6 +173,8 @@ dependencies {
|
|||
implementation(projects.features.manageTokens.impl)
|
||||
implementation(projects.features.send.api)
|
||||
implementation(projects.features.send.impl)
|
||||
implementation(projects.features.sendV2.api)
|
||||
implementation(projects.features.sendV2.impl)
|
||||
implementation(projects.features.qrScanning.api)
|
||||
implementation(projects.features.qrScanning.impl)
|
||||
implementation(projects.features.staking.api)
|
||||
|
|
@ -195,6 +201,8 @@ dependencies {
|
|||
implementation(projects.features.biometry.impl)
|
||||
implementation(projects.features.nft.api)
|
||||
implementation(projects.features.nft.impl)
|
||||
implementation(projects.features.walletconnect.api)
|
||||
implementation(projects.features.walletconnect.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
@ -286,6 +294,7 @@ dependencies {
|
|||
kaptForObfuscatingVariants(deps.retrofit.response.type.keeper)
|
||||
|
||||
/** Testing libraries */
|
||||
testImplementation(projects.common.test)
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.mockk)
|
||||
|
|
@ -308,9 +317,7 @@ dependencies {
|
|||
|
||||
/** Chucker */
|
||||
debugImplementation(deps.chucker)
|
||||
debugPGImplementation(deps.chucker)
|
||||
mockedImplementation(deps.chuckerStub)
|
||||
externalImplementation(deps.chuckerStub)
|
||||
internalImplementation(deps.chuckerStub)
|
||||
releaseImplementation(deps.chuckerStub)
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 8e5de701e02a2d48a68e32cd4783549dde01983e
|
||||
Subproject commit 29f73d909e14cb3760118b197b1929a6088bc40e
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap
|
|||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
|
|
@ -15,6 +14,7 @@ import com.tangem.core.navigation.settings.SettingsManager
|
|||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||
|
|
@ -32,13 +32,13 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
|||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
|
||||
import com.tangem.tap.common.log.TangemAppLoggerInitializer
|
||||
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
||||
|
|
@ -94,8 +94,6 @@ interface ApplicationEntryPoint {
|
|||
|
||||
fun getSaveTwinsOnboardingShownUseCase(): SaveTwinsOnboardingShownUseCase
|
||||
|
||||
fun getWalletNameGenerateUseCase(): GenerateWalletNameUseCase
|
||||
|
||||
fun getCardRepository(): CardRepository
|
||||
|
||||
fun getTangemSdkLogger(): TangemSdkLogger
|
||||
|
|
@ -118,8 +116,6 @@ interface ApplicationEntryPoint {
|
|||
|
||||
fun getTransactionSignerFactory(): TransactionSignerFactory
|
||||
|
||||
fun getGetUserCountryCodeUseCase(): GetUserCountryUseCase
|
||||
|
||||
fun getOnrampFeatureToggles(): OnrampFeatureToggles
|
||||
|
||||
fun getOnboardingV2FeatureToggles(): OnboardingV2FeatureToggles
|
||||
|
|
@ -142,4 +138,8 @@ interface ApplicationEntryPoint {
|
|||
fun getUiMessageSender(): UiMessageSender
|
||||
|
||||
fun getWorkerFactory(): HiltWorkerFactory
|
||||
|
||||
fun getOnlineCardVerifier(): OnlineCardVerifier
|
||||
|
||||
fun getUserWalletBuilderFactory(): UserWalletBuilder.Factory
|
||||
}
|
||||
|
|
@ -299,9 +299,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
|
||||
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
|
||||
// we try to init component with previous stack
|
||||
val routingComponent = routingComponentFactory.create(
|
||||
context = rootComponentContext,
|
||||
initialStack = null,
|
||||
initialStack = appRouterConfig.stack,
|
||||
)
|
||||
|
||||
setContent {
|
||||
|
|
@ -489,7 +492,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
* `android:configChanges="uiMode"` is set in the manifest.
|
||||
* */
|
||||
updateAppBackground()
|
||||
if (routingFeatureToggles.isNavigationRefactoringEnabled.not()) {
|
||||
updateAppBackground()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateAppBackground() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -8,6 +11,7 @@ import coil.ImageLoaderFactory
|
|||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.tangem.Log
|
||||
import com.tangem.TangemSdkLogger
|
||||
import com.tangem.blockchain.common.ExceptionHandler
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.blockchainsdk.BlockchainSDKFactory
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
|
|
@ -46,15 +50,17 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
|||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.operations.attestation.api.TangemApiServiceLogging
|
||||
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
|
||||
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
|
|
@ -68,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
|
||||
|
|
@ -146,9 +150,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
private val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase
|
||||
get() = entryPoint.getSaveTwinsOnboardingShownUseCase()
|
||||
|
||||
private val generateWalletNameUseCase: GenerateWalletNameUseCase
|
||||
get() = entryPoint.getWalletNameGenerateUseCase()
|
||||
|
||||
private val cardRepository: CardRepository
|
||||
get() = entryPoint.getCardRepository()
|
||||
|
||||
|
|
@ -182,9 +183,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
private val transactionSignerFactory: TransactionSignerFactory
|
||||
get() = entryPoint.getTransactionSignerFactory()
|
||||
|
||||
private val getUserCountryUseCase: GetUserCountryUseCase
|
||||
get() = entryPoint.getGetUserCountryCodeUseCase()
|
||||
|
||||
private val onrampFeatureToggles: OnrampFeatureToggles
|
||||
get() = entryPoint.getOnrampFeatureToggles()
|
||||
|
||||
|
|
@ -212,6 +210,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
private val uiMessageSender: UiMessageSender
|
||||
get() = entryPoint.getUiMessageSender()
|
||||
|
||||
private val blockchainExceptionHandler: BlockchainExceptionHandler
|
||||
get() = entryPoint.getBlockchainExceptionHandler()
|
||||
|
||||
private val workerFactory: HiltWorkerFactory
|
||||
get() = entryPoint.getWorkerFactory()
|
||||
|
||||
|
|
@ -220,14 +221,40 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
|
||||
private val onlineCardVerifier: OnlineCardVerifier
|
||||
get() = entryPoint.getOnlineCardVerifier()
|
||||
|
||||
private val userWalletBuilderFactory: UserWalletBuilder.Factory
|
||||
get() = entryPoint.getUserWalletBuilderFactory()
|
||||
|
||||
// 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() {
|
||||
|
|
@ -254,32 +281,48 @@ 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()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
loadNativeLibraries()
|
||||
// ExceptionHandler.append(blockchainExceptionHandler) // TODO [REDACTED_TASK_KEY] Send only to Firebase
|
||||
appScope.launch {
|
||||
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
|
||||
launch(Dispatchers.IO) {
|
||||
loadNativeLibraries()
|
||||
walletConnect2Repository.init(
|
||||
projectId = environmentConfigStorage.getConfigSync().walletConnectProjectId,
|
||||
)
|
||||
updateLogFiles()
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionHandler.append(blockchainExceptionHandler)
|
||||
|
||||
if (LogConfig.network.blockchainSdkNetwork) {
|
||||
BlockchainSdkRetrofitBuilder.interceptors = listOf(
|
||||
createNetworkLoggingInterceptor(),
|
||||
ChuckerInterceptor(this),
|
||||
)
|
||||
TangemApiServiceLogging.addInterceptors(
|
||||
createNetworkLoggingInterceptor(),
|
||||
ChuckerInterceptor(this),
|
||||
)
|
||||
}
|
||||
|
||||
derivationsFinder = DerivationsFinder(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
appStateHolder.mainStore = store
|
||||
|
||||
walletConnect2Repository.init(projectId = environmentConfigStorage.getConfigSync().walletConnectProjectId)
|
||||
appStateHolder.mainStore = store
|
||||
}
|
||||
|
||||
private fun createReduxStore(): Store<AppState> {
|
||||
|
|
@ -301,7 +344,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
generalUserWalletsListManager = generalUserWalletsListManager,
|
||||
wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase,
|
||||
saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase,
|
||||
generateWalletNameUseCase = generateWalletNameUseCase,
|
||||
cardRepository = cardRepository,
|
||||
settingsRepository = settingsRepository,
|
||||
blockchainSDKFactory = blockchainSDKFactory,
|
||||
|
|
@ -312,7 +354,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
shareManager = shareManager,
|
||||
appRouter = appRouter,
|
||||
transactionSignerFactory = transactionSignerFactory,
|
||||
getUserCountryUseCase = getUserCountryUseCase,
|
||||
onrampFeatureToggles = onrampFeatureToggles,
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
|
||||
|
|
@ -322,6 +363,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
clipboardManager = clipboardManager,
|
||||
settingsManager = settingsManager,
|
||||
uiMessageSender = uiMessageSender,
|
||||
onlineCardVerifier = onlineCardVerifier,
|
||||
userWalletBuilderFactory = userWalletBuilderFactory,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -91,8 +91,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
context = context,
|
||||
)
|
||||
}
|
||||
is WalletConnectDialog.ClipboardOrScanQr ->
|
||||
ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
|
||||
is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.data, context)
|
||||
is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context)
|
||||
is WalletConnectDialog.BnbTransactionDialog ->
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.core.analytics.api.ErrorEventLogger
|
||||
import com.tangem.core.analytics.api.ExceptionLogger
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
import timber.log.Timber
|
||||
|
||||
class AnalyticsEventsLogger(
|
||||
private val name: String,
|
||||
private val jsonConverter: MoshiJsonConverter,
|
||||
) : EventLogger, ErrorEventLogger {
|
||||
) : EventLogger, ExceptionLogger {
|
||||
|
||||
override fun logEvent(event: String, params: Map<String, String>) {
|
||||
Timber.d(jsonConverter.prettyPrint(PrintEventModel(name, event, params)))
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
Timber.e(error, jsonConverter.prettyPrint(PrintEventModel(name, "error", params)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.events
|
|||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
class BlockchainExceptionEvent(
|
||||
class BlockchainApiExceptionEvent(
|
||||
selectedHost: String,
|
||||
exceptionHost: String,
|
||||
error: String,
|
||||
|
|
@ -6,7 +6,6 @@ internal sealed class Push(event: String) : AnalyticsEvent(
|
|||
category = "Push",
|
||||
event = event,
|
||||
params = emptyMap(),
|
||||
error = null,
|
||||
) {
|
||||
|
||||
data object PushNotificationOpened : Push(event = "Push Notification Opened")
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ sealed class Settings(
|
|||
category: String = "Settings",
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class ScreenOpened : Settings(event = "Settings Screen Opened")
|
||||
class ButtonStartWalletConnectSession : Settings(event = "Button - Start Wallet Connect Session")
|
||||
|
|
@ -19,16 +18,14 @@ sealed class Settings(
|
|||
sealed class CardSettings(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : Settings("Settings / Card Settings", event, params, error) {
|
||||
) : Settings("Settings / Card Settings", event, params) {
|
||||
|
||||
class ButtonFactoryReset : CardSettings("Button - Factory Reset")
|
||||
class FactoryResetFinished(cardsCount: Int? = null, error: Throwable? = null) : CardSettings(
|
||||
class FactoryResetFinished(cardsCount: Int? = null) : CardSettings(
|
||||
event = "Factory Reset Finished",
|
||||
params = buildMap {
|
||||
cardsCount?.let { put("Cards Count", "$it") }
|
||||
},
|
||||
error = error,
|
||||
)
|
||||
|
||||
class FactoryResetCanceled(cardsCount: Int) : CardSettings(
|
||||
|
|
@ -44,10 +41,9 @@ sealed class Settings(
|
|||
params = mapOf("Type" to type.value),
|
||||
)
|
||||
|
||||
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode, error: Throwable? = null) : CardSettings(
|
||||
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode) : CardSettings(
|
||||
event = "Security Mode Changed",
|
||||
params = mapOf("Mode" to mode.value),
|
||||
error = error,
|
||||
)
|
||||
|
||||
class AccessCodeRecoveryChanged(status: AnalyticsParam.AccessCodeRecoveryStatus) : CardSettings(
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
|
|||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Sign In", event, params, error) {
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
|
||||
class ScreenOpened : SignIn(event = "Sign In Screen Opened")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
|
||||
|
||||
class TangemSdkErrorEvent(
|
||||
val exception: TangemSdkError,
|
||||
) : AnalyticsEvent(
|
||||
category = "TangemSdk",
|
||||
event = "Tangem Sdk Error",
|
||||
params = errorConverter.convert(exception),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val errorConverter = AnalyticsErrorConverter()
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,7 @@ sealed class Token(
|
|||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent(category, event, params, error) {
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
sealed class Receive(
|
||||
event: String,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.O
|
|||
internal sealed class WalletConnect(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Wallet Connect", event, params, error) {
|
||||
) : AnalyticsEvent("Wallet Connect", event, params) {
|
||||
|
||||
class ScreenOpened : WalletConnect(event = "WC Screen Opened")
|
||||
class NewSessionInitiated(source: SourceType) : WalletConnect(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package com.tangem.tap.common.analytics.handlers
|
||||
|
||||
import com.tangem.blockchain.common.ExceptionHandlerOutput
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.events.BlockchainExceptionEvent
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.tap.common.analytics.events.BlockchainApiExceptionEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
class BlockchainExceptionHandler @Inject constructor(
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
||||
) : ExceptionHandlerOutput {
|
||||
override fun handleApiSwitch(currentHost: String, nextHost: String, message: String) {
|
||||
analyticsHandler.send(
|
||||
BlockchainExceptionEvent(
|
||||
analyticsErrorHandler.sendErrorEvent(
|
||||
BlockchainApiExceptionEvent(
|
||||
selectedHost = nextHost,
|
||||
exceptionHost = currentHost,
|
||||
error = message,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
package com.tangem.tap.common.analytics.handlers.firebase
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.api.ErrorEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
|
||||
class FirebaseAnalyticsHandler(
|
||||
private val client: FirebaseAnalyticsClient,
|
||||
) : AnalyticsHandler, ErrorEventHandler {
|
||||
) : AnalyticsHandler, AnalyticsErrorHandler, AnalyticsExceptionHandler {
|
||||
|
||||
private val errorConverter = AnalyticsErrorConverter()
|
||||
|
||||
override fun id(): String = ID
|
||||
|
||||
|
|
@ -18,30 +20,17 @@ class FirebaseAnalyticsHandler(
|
|||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
val error = event.error
|
||||
when {
|
||||
error != null -> {
|
||||
val errorConverter = AnalyticsErrorConverter()
|
||||
if (!errorConverter.canBeHandled(error)) return
|
||||
override fun sendException(event: ExceptionAnalyticsEvent) {
|
||||
if (!errorConverter.canBeHandled(event.exception)) return
|
||||
|
||||
val errorParams = errorConverter.convert(error).toMutableMap()
|
||||
errorParams["Category"] = event.category
|
||||
errorParams["Event"] = event.event
|
||||
errorParams.putAll(event.params)
|
||||
send(error, errorParams)
|
||||
}
|
||||
event is Shop.Purchased -> {
|
||||
send(FirebaseAnalytics.Event.PURCHASE, event.params)
|
||||
}
|
||||
else -> {
|
||||
super.send(event)
|
||||
}
|
||||
}
|
||||
val errorParams = errorConverter.convert(event.exception).toMutableMap()
|
||||
errorParams.putAll(event.params)
|
||||
|
||||
client.logException(event.exception, event.params)
|
||||
}
|
||||
|
||||
override fun send(error: Throwable, params: Map<String, String>) {
|
||||
client.logErrorEvent(error, params)
|
||||
override fun sendErrorEvent(event: AnalyticsEvent) {
|
||||
send(event)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@ import android.os.Bundle
|
|||
import androidx.core.os.bundleOf
|
||||
import com.google.firebase.analytics.ktx.analytics
|
||||
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||
import com.google.firebase.crashlytics.recordException
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.tangem.core.analytics.api.ErrorEventLogger
|
||||
import com.tangem.core.analytics.api.ExceptionLogger
|
||||
import com.tangem.core.analytics.api.EventLogger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface FirebaseAnalyticsClient : EventLogger, ErrorEventLogger
|
||||
interface FirebaseAnalyticsClient : EventLogger, ExceptionLogger
|
||||
|
||||
internal class FirebaseClient : FirebaseAnalyticsClient {
|
||||
|
||||
|
|
@ -27,11 +28,11 @@ internal class FirebaseClient : FirebaseAnalyticsClient {
|
|||
)
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
eventConverter.convertEventParams(params)
|
||||
.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
|
||||
|
||||
fbCrashlytics.recordException(error)
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
fbCrashlytics.recordException(error) {
|
||||
eventConverter.convertEventParams(params)
|
||||
.forEach { key(it.key, it.value) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<String, String>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ internal class FirebaseLogClient(
|
|||
logger.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
logger.logErrorEvent(error, params)
|
||||
override fun logException(error: Throwable, params: Map<String, String>) {
|
||||
logger.logException(error, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,17 +10,30 @@ import com.tangem.common.authentication.AuthenticationManager
|
|||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.Config
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.crypto.bip39.Wordlist
|
||||
import com.tangem.data.card.sdk.CardSdkOwner
|
||||
import com.tangem.data.card.sdk.CardSdkProvider
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironment
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectMap
|
||||
import com.tangem.sdk.DefaultSessionViewDelegate
|
||||
import com.tangem.sdk.api.featuretoggles.CardSdkFeatureToggles
|
||||
import com.tangem.sdk.extensions.*
|
||||
import com.tangem.sdk.nfc.AndroidNfcAvailabilityProvider
|
||||
import com.tangem.sdk.nfc.NfcManager
|
||||
import com.tangem.sdk.storage.create
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -32,8 +45,11 @@ import javax.inject.Singleton
|
|||
*/
|
||||
@Singleton
|
||||
internal class DefaultCardSdkProvider @Inject constructor(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val cardSdkFeatureToggles: CardSdkFeatureToggles,
|
||||
private val apiConfigsManager: ApiConfigsManager,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
) : CardSdkProvider, CardSdkOwner {
|
||||
|
||||
private val observer = Observer()
|
||||
|
|
@ -43,10 +59,26 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
override val sdk: TangemSdk
|
||||
get() = holder?.sdk ?: tryToRegisterWithForegroundActivity()
|
||||
|
||||
init {
|
||||
appPreferencesStore.getObjectMap<ApiEnvironment>(PreferencesKeys.apiConfigsEnvironmentKey)
|
||||
.map { it[ApiConfig.ID.TangemCardSdk.name] == ApiEnvironment.PROD }
|
||||
.onEach { isProd ->
|
||||
holder?.let {
|
||||
it.sdk.config.isTangemAttestationProdEnv = isProd
|
||||
}
|
||||
}
|
||||
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.main))
|
||||
}
|
||||
|
||||
override fun register(activity: FragmentActivity) = runBlocking(dispatchers.mainImmediate) {
|
||||
if (activity.isDestroyed || activity.isFinishing || activity.isChangingConfigurations) {
|
||||
val message = "Tangem SDK owner registration skipped: activity is destroyed or finishing"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(message))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(message),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.info { message }
|
||||
return@runBlocking
|
||||
}
|
||||
|
|
@ -64,14 +96,24 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
|
||||
private fun tryToRegisterWithForegroundActivity(): TangemSdk = runBlocking(dispatchers.mainImmediate) {
|
||||
val warning = "Tangem SDK holder is null, trying to recreate it with foreground activity"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(warning))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(warning),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.warning { warning }
|
||||
|
||||
val activity = foregroundActivityObserver.foregroundActivity
|
||||
|
||||
if (activity == null) {
|
||||
val error = "Tangem SDK holder is null and foreground activity is null"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(error))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(error),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.error { error }
|
||||
error(error)
|
||||
}
|
||||
|
|
@ -82,7 +124,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
|
||||
if (sdk == null) {
|
||||
val error = "Tangem SDK is null after re-registering with foreground activity"
|
||||
analyticsEventHandler.send(TangemSdkWarningEvent(error))
|
||||
analyticsExceptionHandler.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = IllegalStateException(error),
|
||||
params = errorParams,
|
||||
),
|
||||
)
|
||||
Log.error { error }
|
||||
error(error)
|
||||
}
|
||||
|
|
@ -108,7 +155,12 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
authenticationManager = authenticationManager,
|
||||
keystoreManager = keystoreManager,
|
||||
wordlist = Wordlist.getWordlist(activity),
|
||||
config = config,
|
||||
config = config.apply {
|
||||
isNewOnlineAttestationEnabled = cardSdkFeatureToggles.isNewAttestationEnabled
|
||||
|
||||
val apiConfig = apiConfigsManager.getEnvironmentConfig(id = ApiConfig.ID.TangemCardSdk)
|
||||
isTangemAttestationProdEnv = apiConfig.environment == ApiEnvironment.PROD
|
||||
},
|
||||
)
|
||||
|
||||
holder = Holder(
|
||||
|
|
@ -161,7 +213,6 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
|
||||
val config = Config(
|
||||
linkedTerminal = true,
|
||||
allowUntrustedCards = true,
|
||||
filter = CardFilter(
|
||||
allowedCardTypes = FirmwareVersion.FirmwareType.entries.toList(),
|
||||
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33),
|
||||
|
|
@ -170,5 +221,10 @@ internal class DefaultCardSdkProvider @Inject constructor(
|
|||
),
|
||||
),
|
||||
)
|
||||
|
||||
val errorParams = mapOf(
|
||||
"Category" to "Tangem SDK",
|
||||
"Event" to "Warning",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ internal class RuntimeUserWalletsStore(
|
|||
override val userWallets: Flow<List<UserWallet>>
|
||||
get() = userWalletsListManager.userWallets
|
||||
|
||||
override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? {
|
||||
return userWalletsListManager
|
||||
.userWallets
|
||||
.firstOrNull()
|
||||
?.singleOrNull { it.walletId == key }
|
||||
override fun getSyncOrNull(key: UserWalletId): UserWallet? {
|
||||
return userWalletsListManager.userWalletsSync.firstOrNull { it.walletId == key }
|
||||
}
|
||||
|
||||
override suspend fun getSyncStrict(key: UserWalletId): UserWallet {
|
||||
return requireNotNull(getSyncOrNull(key)) { "Unable to find user wallet with provided ID: $key" }
|
||||
}
|
||||
|
||||
override suspend fun getAllSyncOrNull(): List<UserWallet>? {
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.tap.data
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
internal class TangemSdkWarningEvent(message: String) : AnalyticsEvent(
|
||||
category = "Tangem SDK",
|
||||
event = "Warning",
|
||||
error = IllegalStateException(message),
|
||||
)
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.di
|
|||
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
|
|
@ -55,7 +54,6 @@ internal object ActivityModule {
|
|||
excludedBlockchains: ExcludedBlockchains,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
onrampFeatureToggles: OnrampFeatureToggles,
|
||||
expressAssetsStore: ExpressAssetsStore,
|
||||
): RampStateManager {
|
||||
return DefaultRampManager(
|
||||
exchangeService = appStateHolder.exchangeService,
|
||||
|
|
@ -67,7 +65,6 @@ internal object ActivityModule {
|
|||
excludedBlockchains = excludedBlockchains,
|
||||
dispatchers = dispatchers,
|
||||
onrampFeatureToggles = onrampFeatureToggles,
|
||||
expressAssetsStore = expressAssetsStore,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@ import com.tangem.domain.card.repository.DerivationsRepository
|
|||
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.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -69,6 +72,8 @@ internal object ManageTokensDomainModule {
|
|||
derivationsRepository: DerivationsRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): SaveManagedTokensUseCase {
|
||||
return SaveManagedTokensUseCase(
|
||||
customTokensRepository = customTokensRepository,
|
||||
|
|
@ -78,6 +83,8 @@ internal object ManageTokensDomainModule {
|
|||
derivationsRepository = derivationsRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -108,4 +115,12 @@ internal object ManageTokensDomainModule {
|
|||
fun provideCheckCurrencyUnsupportedUseCase(repository: ManageTokensRepository): CheckCurrencyUnsupportedUseCase {
|
||||
return CheckCurrencyUnsupportedUseCase(repository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDistinctManagedCurrenciesTokenUseCase(
|
||||
coroutineDispatchersProvider: CoroutineDispatcherProvider,
|
||||
): GetDistinctManagedCurrenciesUseCase {
|
||||
return GetDistinctManagedCurrenciesUseCase(coroutineDispatchersProvider)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,9 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
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.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
|
|
@ -60,6 +62,8 @@ object MarketsDomainModule {
|
|||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): SaveMarketTokensUseCase {
|
||||
return SaveMarketTokensUseCase(
|
||||
derivationsRepository = derivationsRepository,
|
||||
|
|
@ -68,6 +72,8 @@ object MarketsDomainModule {
|
|||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.nft.*
|
||||
import com.tangem.domain.nft.repository.NFTRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
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 NFTDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGetNFTCollectionsUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
nftRepository: NFTRepository,
|
||||
): GetNFTCollectionsUseCase = GetNFTCollectionsUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
nftRepository = nftRepository,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFetchNFTCollectionsUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
nftRepository: NFTRepository,
|
||||
): FetchNFTCollectionsUseCase = FetchNFTCollectionsUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
nftRepository = nftRepository,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFetchNFTCollectionAssetsUseCase(nftRepository: NFTRepository): FetchNFTCollectionAssetsUseCase =
|
||||
FetchNFTCollectionAssetsUseCase(
|
||||
nftRepository = nftRepository,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGetNFTAvailableNetworksUseCase(
|
||||
nftRepository: NFTRepository,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
): GetNFTAvailableNetworksUseCase = GetNFTAvailableNetworksUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
nftRepository = nftRepository,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFilterNFTAvailableNetworksUseCase(
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): FilterNFTAvailableNetworksUseCase = FilterNFTAvailableNetworksUseCase(
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGetNFTNetworkStatusUseCase(networksRepository: NetworksRepository): GetNFTNetworkStatusUseCase =
|
||||
GetNFTNetworkStatusUseCase(
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.onramp.repositories.LegacyTopUpRepository
|
||||
import com.tangem.domain.onramp.*
|
||||
import com.tangem.domain.onramp.repositories.HotCryptoRepository
|
||||
import com.tangem.domain.onramp.repositories.OnrampErrorResolver
|
||||
|
|
@ -242,4 +243,10 @@ internal object OnrampDomainModule {
|
|||
fun provideFetchHotCryptoUseCase(hotCryptoRepository: HotCryptoRepository): FetchHotCryptoUseCase {
|
||||
return FetchHotCryptoUseCase(hotCryptoRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetLegacyTopUpUrlUseCase(legacyTopUpRepository: LegacyTopUpRepository): GetLegacyTopUpUrlUseCase {
|
||||
return GetLegacyTopUpUrlUseCase(legacyTopUpRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.repositories.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -209,4 +210,12 @@ internal object StakingDomainModule {
|
|||
fun provideGetStakingIntegrationIdUseCase(stakingRepository: StakingRepository): GetStakingIntegrationIdUseCase {
|
||||
return GetStakingIntegrationIdUseCase(stakingRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCheckAccountInitializedUseCase(
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
): CheckAccountInitializedUseCase {
|
||||
return CheckAccountInitializedUseCase(walletManagersFacade)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,18 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
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.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.operations.*
|
||||
import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations
|
||||
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.features.swap.SwapFeatureToggles
|
||||
import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
|
|
@ -29,12 +34,16 @@ internal object TokensDomainModule {
|
|||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): AddCryptoCurrenciesUseCase {
|
||||
return AddCryptoCurrenciesUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -45,8 +54,17 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): FetchTokenListUseCase {
|
||||
return FetchTokenListUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
|
||||
return FetchTokenListUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -149,8 +167,17 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): FetchCurrencyStatusUseCase {
|
||||
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
|
||||
return FetchCurrencyStatusUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -160,8 +187,17 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): FetchCardTokenListUseCase {
|
||||
return FetchCardTokenListUseCase(currenciesRepository, networksRepository, quotesRepository, stakingRepository)
|
||||
return FetchCardTokenListUseCase(
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -209,7 +245,6 @@ internal object TokensDomainModule {
|
|||
currenciesRepository: CurrenciesRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
promoRepository: PromoRepository,
|
||||
swapFeatureToggles: SwapFeatureToggles,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
currencyStatusOperations: BaseCurrencyStatusOperations,
|
||||
): GetCryptoCurrencyActionsUseCase {
|
||||
|
|
@ -219,7 +254,6 @@ internal object TokensDomainModule {
|
|||
currenciesRepository = currenciesRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
promoRepository = promoRepository,
|
||||
swapFeatureToggles = swapFeatureToggles,
|
||||
dispatchers = dispatchers,
|
||||
currencyStatusOperations = currencyStatusOperations,
|
||||
)
|
||||
|
|
@ -273,9 +307,13 @@ internal object TokensDomainModule {
|
|||
@Singleton
|
||||
fun provideUpdateDelayedCurrencyStatusUseCase(
|
||||
networksRepository: NetworksRepository,
|
||||
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): UpdateDelayedNetworkStatusUseCase {
|
||||
return UpdateDelayedNetworkStatusUseCase(
|
||||
networksRepository = networksRepository,
|
||||
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -350,22 +388,20 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
|
||||
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
): BaseCurrenciesStatusesOperations {
|
||||
return if (tokensFeatureToggles.isBalancesCachingEnabled) {
|
||||
CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
} else {
|
||||
LceCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
return CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
|
||||
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -376,21 +412,19 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
stakingRepository: StakingRepository,
|
||||
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
|
||||
multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
|
||||
multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
): BaseCurrencyStatusOperations {
|
||||
return if (tokensFeatureToggles.isBalancesCachingEnabled) {
|
||||
CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
} else {
|
||||
CurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
return CachedCurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
stakingRepository = stakingRepository,
|
||||
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
|
||||
multiNetworkStatusSupplier = multiNetworkStatusSupplier,
|
||||
multiNetworkStatusFetcher = multiNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ package com.tangem.tap.di.domain
|
|||
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusFetcher
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.transaction.FeeRepository
|
||||
|
|
@ -33,12 +36,16 @@ internal object TransactionDomainModule {
|
|||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
transactionRepository: TransactionRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
singleNetworkStatusFetcher: SingleNetworkStatusFetcher,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): SendTransactionUseCase {
|
||||
return SendTransactionUseCase(
|
||||
demoConfig = DemoConfig(),
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
transactionRepository = transactionRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
singleNetworkStatusFetcher = singleNetworkStatusFetcher,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -49,12 +56,16 @@ internal object TransactionDomainModule {
|
|||
walletManagersFacade: WalletManagersFacade,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
|
||||
tokensFeatureToggles: TokensFeatureToggles,
|
||||
): AssociateAssetUseCase {
|
||||
return AssociateAssetUseCase(
|
||||
cardSdkConfigRepository = cardSdkConfigRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
currenciesRepository = currenciesRepository,
|
||||
networksRepository = networksRepository,
|
||||
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
|
||||
tokensFeatureToggles = tokensFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,12 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
|
||||
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
|
||||
|
|
@ -18,6 +23,7 @@ import dagger.hilt.InstallIn
|
|||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object WalletsDomainModule {
|
||||
|
|
@ -165,4 +171,30 @@ internal object WalletsDomainModule {
|
|||
fun providesSeedPhraseNotificationUseCase(walletsRepository: WalletsRepository): SeedPhraseNotificationUseCase {
|
||||
return SeedPhraseNotificationUseCase(walletsRepository = walletsRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetCardImageUseCase(
|
||||
onlineCardVerifier: OnlineCardVerifier,
|
||||
cardArtworksProvider: CardArtworksProvider,
|
||||
cardSdkFeatureToggles: CardSdkFeatureToggles,
|
||||
): GetCardImageUseCase {
|
||||
return GetCardImageUseCase(
|
||||
verifier = onlineCardVerifier,
|
||||
cardArtworksProvider = cardArtworksProvider,
|
||||
cardSdkFeatureToggles = cardSdkFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesIsWalletNFTEnabledSyncUseCase(
|
||||
walletsRepository: WalletsRepository,
|
||||
nftFeatureToggles: NFTFeatureToggles,
|
||||
): IsWalletNFTEnabledSyncUseCase {
|
||||
return IsWalletNFTEnabledSyncUseCase(
|
||||
walletsRepository = walletsRepository,
|
||||
nftFeatureToggles = nftFeatureToggles,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.di.routing
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.tap.routing.ProxyAppRouter
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.tap.routing.configurator.MutableAppRouterConfig
|
||||
|
|
@ -17,8 +18,15 @@ internal object AppRouterModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppRouter(config: AppRouterConfig, dispatchers: CoroutineDispatcherProvider): AppRouter =
|
||||
ProxyAppRouter(config, dispatchers)
|
||||
fun provideAppRouter(
|
||||
config: AppRouterConfig,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
): AppRouter = ProxyAppRouter(
|
||||
config = config,
|
||||
dispatchers = dispatchers,
|
||||
analyticsExceptionHandler = analyticsExceptionHandler,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
|||
|
|
@ -6,14 +6,18 @@ 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.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.CardArtworksProvider
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
|
||||
|
||||
fun CardDTO.signedHashesCount(): Int {
|
||||
return wallets.sumOf { it.totalSignedHashes ?: 0 }
|
||||
}
|
||||
|
||||
suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null): String {
|
||||
suspend fun CardDTO.getOrLoadCardArtworkUrl(
|
||||
cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null,
|
||||
onlineCardVerifier: OnlineCardVerifier,
|
||||
): String {
|
||||
fun ifAnyError(): String {
|
||||
return when {
|
||||
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
|
||||
|
|
@ -28,13 +32,13 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
|
|||
}
|
||||
}
|
||||
|
||||
return when (val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)) {
|
||||
return when (val cardInfoResult = cardInfo ?: onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
|
||||
is Result.Success -> {
|
||||
val artworkId = cardInfoResult.data.artwork?.id
|
||||
if (artworkId.isNullOrEmpty()) {
|
||||
ifAnyError()
|
||||
} else {
|
||||
OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
|
||||
CardArtworksProvider.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -235,13 +235,25 @@ internal class LegacyScanProcessor @Inject constructor(
|
|||
} else {
|
||||
Analytics.setContext(scanResponse)
|
||||
|
||||
val isTwinRefactoringEnabled =
|
||||
store.inject(DaggerGraphState::onboardingV2FeatureToggles).isTwinRefactoringEnabled
|
||||
|
||||
val wasTwinsOnboardingShown =
|
||||
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()
|
||||
|
||||
if (scanResponse.twinsIsTwinned() && !wasTwinsOnboardingShown) {
|
||||
onWalletNotCreated()
|
||||
store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse)))
|
||||
navigateTo(AppRoute.OnboardingTwins) { onProgressStateChange(it) }
|
||||
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) }
|
||||
}
|
||||
} else {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
onSuccess(scanResponse)
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.domain.card.ScanCardException
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
|
|
@ -34,7 +36,7 @@ internal object UseCaseScanProcessor {
|
|||
ifLeft = {
|
||||
val error = scanCardExceptionConverter.convertBack(it)
|
||||
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendErrorEvent(TangemSdkErrorEvent(error))
|
||||
CompletionResult.Failure(error)
|
||||
},
|
||||
ifRight = { CompletionResult.Success(it) },
|
||||
|
|
@ -97,7 +99,12 @@ internal object UseCaseScanProcessor {
|
|||
-> {
|
||||
val error = scanCardExceptionConverter.convertBack(exception)
|
||||
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendException(
|
||||
ExceptionAnalyticsEvent(
|
||||
exception = error,
|
||||
params = mapOf("Event" to "Scan"),
|
||||
),
|
||||
)
|
||||
onFailure(error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,15 +59,27 @@ class CheckForOnboardingChain(
|
|||
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) {
|
||||
store.dispatchOnMain(
|
||||
TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(previousChainResult)),
|
||||
)
|
||||
ScanChainException.OnboardingNeeded(AppRoute.OnboardingTwins).left()
|
||||
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()
|
||||
}
|
||||
} else {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
previousChainResult.right()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ internal class ScanCardExceptionConverter : TwoWayConverter<TangemError, ScanCar
|
|||
else -> ScanCardException.UnknownException(value)
|
||||
}
|
||||
|
||||
override fun convertBack(value: ScanCardException): TangemError {
|
||||
override fun convertBack(value: ScanCardException): TangemSdkError {
|
||||
return when (value) {
|
||||
is ScanCardException.UnknownException -> TangemSdkError.ExceptionError(value)
|
||||
is ScanCardException.UserCancelled -> TangemSdkError.UserCancelled()
|
||||
|
|
@ -31,7 +31,7 @@ internal class ScanCardExceptionConverter : TwoWayConverter<TangemError, ScanCar
|
|||
else -> ScanCardException.UnknownException(value)
|
||||
}
|
||||
|
||||
private fun concertScanChainException(value: ScanCardException.ChainException): TangemError {
|
||||
private fun concertScanChainException(value: ScanCardException.ChainException): TangemSdkError {
|
||||
return when (val e = value as? ScanChainException) {
|
||||
is ScanChainException.DisclaimerWasCanceled -> TangemSdkError.UserCancelled()
|
||||
is ScanChainException.OnboardingNeeded,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.common.extensions.ByteArrayKey
|
|||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.common.usersCode.UserCodeRepository
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.res.getStringSafe
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
|
|
@ -41,6 +40,7 @@ import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
|||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.sdk.api.visa.VisaCardActivationResponse
|
||||
import com.tangem.sdk.api.visa.VisaCardActivationTaskMode
|
||||
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.ResetBackupCardTask
|
||||
|
|
@ -224,7 +224,7 @@ internal class DefaultTangemSdkManager(
|
|||
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
|
||||
if (result is CompletionResult.Failure) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
Analytics.sendErrorEvent(TangemSdkErrorEvent(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ object Wallet2MockContent : MockContent {
|
|||
name = "TANGEM 2.0",
|
||||
publicKey = byteArrayOf(2, -120, 89, -52, -60, 36, -73, -17, 103, 107, -110, -36, 3, 110, -122, 72, 43, -38, 8, 30, -50, 25, -23, -17, 38, 94, 5, -112, -20, 9, 54, -24, -32),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM SDK",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ object WalletMockContent : MockContent {
|
|||
name = "TANGEM AG",
|
||||
publicKey = byteArrayOf(3, 86, -25, -61, 55, 99, 41, -33, -82, 115, -120, -33, 22, -107, 103, 3, -122, 16, 60, -110, 72, 106, -121, 100, 79, -87, -27, 18, -55, -49, 78, -110, -2),
|
||||
),
|
||||
manufacturer = Card.Manufacturer(
|
||||
name = "TANGEM",
|
||||
manufactureDate = Date(1743759687),
|
||||
signature = byteArrayOf(),
|
||||
),
|
||||
walletCurves = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519, EllipticCurve.Bls12381G2Aug),
|
||||
firmwareVersion = FirmwareVersion(
|
||||
major = 4,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ 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.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.map
|
||||
import com.tangem.common.timemeasure.RealtimeMonotonicTimeSource
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
import com.tangem.core.error.ext.tangemError
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||
import com.tangem.datasource.local.visa.VisaOTPStorage
|
||||
import com.tangem.datasource.local.visa.VisaOtpData
|
||||
|
|
@ -22,11 +24,14 @@ import com.tangem.datasource.local.visa.hasSavedOTP
|
|||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
import com.tangem.domain.visa.error.VisaAuthorizationAPIError
|
||||
import com.tangem.domain.visa.model.*
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
||||
import com.tangem.operations.GenerateOTPCommand
|
||||
import com.tangem.operations.attestation.AttestCardKeyCommand
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.operations.sign.SignHashResponse
|
||||
|
|
@ -52,6 +57,8 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||
) : CardSessionRunnable<VisaCardActivationResponse> {
|
||||
|
||||
override val allowsRequestAccessCodeFromRepository: Boolean = false
|
||||
|
||||
private class SessionContext(
|
||||
val visaActivationRepository: VisaActivationRepository,
|
||||
val cardId: String,
|
||||
|
|
@ -68,7 +75,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||
|
||||
if (card.cardId != activationInput.cardId) {
|
||||
return CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.WrongCard.message))
|
||||
return CompletionResult.Failure(VisaActivationError.WrongCard.tangemError)
|
||||
}
|
||||
|
||||
val visaActivationRepository = visaActivationRepositoryFactory.create(
|
||||
|
|
@ -101,7 +108,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
private suspend fun SessionContext.signAuthorizationChallenge(
|
||||
challengeToSign: VisaAuthChallenge.Card,
|
||||
): CompletionResult<VisaCardActivationResponse> {
|
||||
val attestationCommand = AttestCardKeyCommand(challenge = CryptoUtils.generateRandomBytes(length = 16))
|
||||
val attestationCommand = AttestCardKeyCommand(challenge = challengeToSign.challenge.hexToBytes())
|
||||
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
attestationCommand.run(session = session) { attestationResponse ->
|
||||
|
|
@ -149,7 +156,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
|
||||
private suspend fun SessionContext.getDataToSign(
|
||||
signedChallenge: VisaAuthSignedChallenge,
|
||||
): Either<TangemSdkError.Underlying, VisaDataToSignByCardWallet> = either {
|
||||
): Either<TangemError, VisaDataToSignByCardWallet> = either {
|
||||
catch(
|
||||
block = {
|
||||
val tokens = visaAuthRepository.getAccessTokens(signedChallenge)
|
||||
|
|
@ -158,13 +165,13 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
|
||||
val remoteState = visaActivationRepository.getActivationRemoteState()
|
||||
if (remoteState !is VisaActivationRemoteState.CardWalletSignatureRequired) {
|
||||
raise(TangemSdkError.Underlying(VisaActivationError.WrongRemoteState.message))
|
||||
raise(VisaActivationError.WrongRemoteState.tangemError)
|
||||
}
|
||||
|
||||
visaActivationRepository.getCardWalletAcceptanceData(remoteState.request)
|
||||
},
|
||||
catch = {
|
||||
raise(TangemSdkError.Underlying("Underlying network error: ${it.message ?: ""}"))
|
||||
raise(VisaAuthorizationAPIError.tangemError)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -245,11 +252,17 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
|
||||
?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||
|
||||
val derivedPublicKey = wallet.derivedKeys[VisaUtilities.visaDefaultDerivationPath]
|
||||
?: return CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message))
|
||||
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(TangemSdkError.Underlying(it.message)) }
|
||||
.getOrElse { return CompletionResult.Failure(it.tangemError) }
|
||||
.value
|
||||
|
||||
val task = SignHashCommand(
|
||||
|
|
@ -284,6 +297,20 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun SessionContext.deriveKey(publicKey: ByteArray): CompletionResult<ExtendedPublicKey> {
|
||||
val derivationPath = VisaUtilities.visaDefaultDerivationPath
|
||||
?: return CompletionResult.Failure(VisaActivationError.FailedToCreateAddress.tangemError)
|
||||
|
||||
val derivationTask = DeriveWalletPublicKeyTask(publicKey, derivationPath)
|
||||
val derivationTaskResult = suspendCancellableCoroutine { continuation ->
|
||||
derivationTask.run(session) { result ->
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
|
||||
return derivationTaskResult
|
||||
}
|
||||
|
||||
private suspend fun SessionContext.handleSignedData(
|
||||
dataToSign: VisaDataToSignByCardWallet,
|
||||
walletAddress: String,
|
||||
|
|
@ -291,7 +318,7 @@ class VisaCardActivationTask @AssistedInject constructor(
|
|||
): CompletionResult<VisaCardActivationResponse> {
|
||||
val otp = otpStorage.getOTP(cardId) ?: run {
|
||||
createOTP()
|
||||
otpStorage.getOTP(cardId) ?: return CompletionResult.Failure(TangemSdkError.Underlying("OTP not found"))
|
||||
otpStorage.getOTP(cardId) ?: return CompletionResult.Failure(VisaActivationError.MissingRootOTP.tangemError)
|
||||
}
|
||||
|
||||
val signedActivationData = dataToSign.sign(
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.common.core.CompletionCallback
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.core.error.ext.tangemError
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
|
|
@ -18,7 +19,7 @@ import com.tangem.domain.common.visa.VisaUtilities
|
|||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility.findKeyWithoutDerivation
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.visa.model.VisaActivationError
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.domain.visa.model.VisaSignedDataByCustomerWallet
|
||||
import com.tangem.domain.visa.model.sign
|
||||
|
|
@ -37,6 +38,7 @@ class VisaCustomerWalletApproveTask(
|
|||
}
|
||||
|
||||
if (VisaUtilities.isVisaCard(card.firmwareVersion.doubleValue, card.batchId)) {
|
||||
// TODO TVF-21
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying("Can't use Visa card for approve")))
|
||||
return
|
||||
}
|
||||
|
|
@ -44,7 +46,7 @@ class VisaCustomerWalletApproveTask(
|
|||
if (visaDataForApprove.customerWalletCardId != null && card.cardId != visaDataForApprove.customerWalletCardId) {
|
||||
callback(
|
||||
CompletionResult.Failure(
|
||||
TangemSdkError.Underlying("Use tangem wallet specified during visa registration"),
|
||||
TangemSdkError.Underlying("Use tangem wallet specified during visa registration"), // TODO TVF-21
|
||||
),
|
||||
)
|
||||
return
|
||||
|
|
@ -74,16 +76,12 @@ class VisaCustomerWalletApproveTask(
|
|||
}
|
||||
|
||||
val derivationPath = VisaUtilities.visaDefaultDerivationPath(derivationStyle) ?: run {
|
||||
callback(
|
||||
CompletionResult.Failure(
|
||||
TangemSdkError.Underlying("Failed to generate derivation path with provided derivation style"),
|
||||
),
|
||||
)
|
||||
callback(CompletionResult.Failure(VisaActivationError.FailedToCreateAddress.tangemError))
|
||||
return
|
||||
}
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message)))
|
||||
callback(CompletionResult.Failure(VisaActivationError.MissingWallet.tangemError))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +121,7 @@ class VisaCustomerWalletApproveTask(
|
|||
)
|
||||
|
||||
validationResult.onLeft {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message)))
|
||||
callback(CompletionResult.Failure(it.tangemError))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +142,7 @@ class VisaCustomerWalletApproveTask(
|
|||
targetAddress = visaDataForApprove.targetAddress,
|
||||
card = CardDTO(card),
|
||||
).getOrElse {
|
||||
callback(CompletionResult.Failure(TangemSdkError.Underlying(it.message)))
|
||||
callback(CompletionResult.Failure(it.tangemError))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@ internal class DefaultTokensFeatureToggles(
|
|||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : TokensFeatureToggles {
|
||||
|
||||
override val isBalancesCachingEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "BALANCES_CACHING_ENABLED")
|
||||
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")
|
||||
}
|
||||
|
|
@ -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<String>,
|
||||
@Json(name = "scanResponse")
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@ internal class DefaultUserWalletsPublicInformationRepository(
|
|||
moshi: Moshi,
|
||||
private val secureStorage: SecureStorage,
|
||||
) : UserWalletsPublicInformationRepository {
|
||||
private val publicInformationAdapter: JsonAdapter<List<UserWalletPublicInformation>> = moshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, UserWalletPublicInformation::class.java),
|
||||
)
|
||||
|
||||
private val publicInformationAdapter: JsonAdapter<List<UserWalletPublicInformation>> by lazy {
|
||||
moshi.adapter(
|
||||
Types.newParameterizedType(List::class.java, UserWalletPublicInformation::class.java),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult<Unit> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,15 @@ internal class DefaultUserWalletsSensitiveInformationRepository(
|
|||
private val secureStorage: SecureStorage,
|
||||
) : UserWalletsSensitiveInformationRepository {
|
||||
|
||||
private val sensitiveInformationAdapter: JsonAdapter<UserWalletSensitiveInformation> = moshi.adapter(
|
||||
UserWalletSensitiveInformation::class.java,
|
||||
)
|
||||
private val encryptedSensitiveInformationMapAdapter: JsonAdapter<Map<String, ByteArray>> = moshi.adapter(
|
||||
Types.newParameterizedType(Map::class.java, String::class.java, ByteArray::class.java),
|
||||
)
|
||||
private val sensitiveInformationAdapter: JsonAdapter<UserWalletSensitiveInformation> by lazy {
|
||||
moshi.adapter(UserWalletSensitiveInformation::class.java)
|
||||
}
|
||||
|
||||
private val encryptedSensitiveInformationMapAdapter: JsonAdapter<Map<String, ByteArray>> by lazy {
|
||||
moshi.adapter(
|
||||
Types.newParameterizedType(Map::class.java, String::class.java, ByteArray::class.java),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun save(userWallet: UserWallet, encryptionKey: ByteArray?): CompletionResult<Unit> {
|
||||
if (encryptionKey == null) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ internal val UserWallet.publicInformation: UserWalletPublicInformation
|
|||
get() = UserWalletPublicInformation(
|
||||
name = name,
|
||||
walletId = walletId,
|
||||
artworkUrl = artworkUrl,
|
||||
cardsInWallet = cardsInWallet,
|
||||
isMultiCurrency = isMultiCurrency,
|
||||
scanResponse = scanResponse.copy(
|
||||
|
|
@ -31,7 +30,6 @@ internal fun UserWalletPublicInformation.toUserWallet(): UserWallet {
|
|||
return UserWallet(
|
||||
name = name,
|
||||
walletId = walletId,
|
||||
artworkUrl = artworkUrl,
|
||||
cardsInWallet = cardsInWallet,
|
||||
scanResponse = scanResponse,
|
||||
isMultiCurrency = isMultiCurrency,
|
||||
|
|
|
|||
|
|
@ -8,11 +8,15 @@ import com.tangem.common.core.CardSession
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.core.error.ext.tangemError
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||
import com.tangem.domain.common.visa.VisaUtilities
|
||||
import com.tangem.domain.common.visa.VisaWalletPublicKeyUtility
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
import com.tangem.domain.visa.error.VisaAuthorizationAPIError
|
||||
import com.tangem.domain.visa.error.VisaCardScanError
|
||||
import com.tangem.domain.visa.model.*
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.domain.visa.repository.VisaAuthRepository
|
||||
|
|
@ -71,9 +75,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
val derivationPath = VisaUtilities.visaDefaultDerivationPath ?: run {
|
||||
Timber.e("Failed to create derivation path while first scan")
|
||||
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToCreateDerivationPath.errorDescription),
|
||||
)
|
||||
return CompletionResult.Failure(VisaCardScanError.FailedToCreateDerivationPath.tangemError)
|
||||
}
|
||||
|
||||
val derivationTask = DeriveWalletPublicKeyTask(wallet.publicKey, derivationPath)
|
||||
|
|
@ -104,32 +106,24 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
|
||||
val derivationPath = VisaUtilities.visaDefaultDerivationPath ?: run {
|
||||
Timber.e("Failed to create derivation path while handling wallet authorization")
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToCreateDerivationPath.errorDescription),
|
||||
)
|
||||
return CompletionResult.Failure(VisaCardScanError.FailedToCreateDerivationPath.tangemError)
|
||||
}
|
||||
|
||||
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
|
||||
|
||||
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
|
||||
Timber.e("Failed to find extended public key while handling wallet authorization")
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToFindDerivedWalletKey.errorDescription),
|
||||
)
|
||||
return CompletionResult.Failure(VisaCardScanError.FailedToFindWallet.tangemError)
|
||||
}
|
||||
|
||||
val extendedPublicKey = wallet.derivedKeys[derivationPath] ?: run {
|
||||
Timber.e("Failed to find extended public key while handling wallet authorization")
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToFindDerivedWalletKey.errorDescription),
|
||||
)
|
||||
return CompletionResult.Failure(VisaCardScanError.FailedToFindDerivedWalletKey.tangemError)
|
||||
}
|
||||
|
||||
val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(extendedPublicKey.publicKey)
|
||||
.getOrElse {
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying("Cannot generate address on Visa curve"),
|
||||
)
|
||||
return CompletionResult.Failure(it.tangemError)
|
||||
}
|
||||
|
||||
Timber.i("Requesting challenge for wallet authorization")
|
||||
|
|
@ -200,7 +194,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
)
|
||||
}.getOrElse {
|
||||
Timber.e("Failed to get challenge for Card authorization. Plain error: ${it.message}")
|
||||
return CompletionResult.Failure(TangemSdkError.Underlying(it.message ?: "Unknown error"))
|
||||
return CompletionResult.Failure(VisaAuthorizationAPIError.tangemError)
|
||||
}
|
||||
|
||||
Timber.i("Received challenge to sign: ${challengeResponse.challenge}")
|
||||
|
|
@ -230,11 +224,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
)
|
||||
}.getOrElse {
|
||||
Timber.e("Failed to sign challenge with Card public key. Plain error: ${it.message}")
|
||||
return CompletionResult.Failure(
|
||||
TangemSdkError.Underlying(
|
||||
customMessage = it.message ?: "Unknown error",
|
||||
),
|
||||
)
|
||||
return CompletionResult.Failure(VisaAuthorizationAPIError.tangemError)
|
||||
}
|
||||
|
||||
visaAuthTokenStorage.store(
|
||||
|
|
@ -251,7 +241,7 @@ internal class VisaCardScanHandler @Inject constructor(
|
|||
}
|
||||
|
||||
if (error != null) {
|
||||
return CompletionResult.Failure(TangemSdkError.Underlying(error.message))
|
||||
return CompletionResult.Failure(error.tangemError)
|
||||
}
|
||||
|
||||
val activationInput = VisaActivationInput(
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.tap.domain.visa
|
||||
|
||||
private const val COMMON_DESCRIPTION = "Error occurred. Please contact support."
|
||||
|
||||
internal enum class VisaCardScanHandlerError(val errorDescription: String) {
|
||||
FailedToCreateDerivationPath(COMMON_DESCRIPTION),
|
||||
FailedToFindWallet(COMMON_DESCRIPTION),
|
||||
FailedToFindDerivedWalletKey(COMMON_DESCRIPTION),
|
||||
}
|
||||
|
|
@ -4,18 +4,11 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.Account
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
|
||||
internal class TangemWcBlockchainHelper(
|
||||
featureToggles: WalletConnectFeatureToggles,
|
||||
) : WcBlockchainHelper {
|
||||
internal class TangemWcBlockchainHelper : WcBlockchainHelper {
|
||||
|
||||
private val supportedNonEvmBlockchains = if (featureToggles.isSolanaTxSignEnabled) {
|
||||
setOf(Blockchain.Solana, Blockchain.SolanaTestnet)
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
private val supportedNonEvmBlockchains = setOf(Blockchain.Solana, Blockchain.SolanaTestnet)
|
||||
|
||||
override fun chainIdToNetworkIdOrNull(chainId: String): String? {
|
||||
val parsedId = chainId.parseId() ?: return null
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import com.reown.android.relay.ConnectionType
|
|||
import com.reown.walletkit.client.Wallet
|
||||
import com.reown.walletkit.client.WalletKit
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.data.walletconnect.pair.unsupportedDApps
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
import com.tangem.tap.common.analytics.events.WalletConnect
|
||||
import com.tangem.tap.domain.walletconnect2.app.TangemWcBlockchainHelper
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
|
|
@ -15,7 +17,6 @@ import com.tangem.tap.domain.walletconnect2.domain.WcJrpcMethods
|
|||
import com.tangem.tap.domain.walletconnect2.domain.WcJrpcRequestsDeserializer
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcRequest
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -27,7 +28,6 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
private val application: Application,
|
||||
private val wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
) : LegacyWalletConnectRepository {
|
||||
|
||||
private var sessionProposal: Wallet.Model.SessionProposal? = null
|
||||
|
|
@ -39,7 +39,7 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
|
||||
private val _activeSessions: MutableSharedFlow<List<WalletConnectSession>> = MutableSharedFlow()
|
||||
override val activeSessions: Flow<List<WalletConnectSession>> = _activeSessions
|
||||
private val blockchainHelper by lazy { TangemWcBlockchainHelper(walletConnectFeatureToggles) }
|
||||
private val blockchainHelper by lazy { TangemWcBlockchainHelper() }
|
||||
|
||||
override var currentSessions: List<WalletConnectSession> = emptyList()
|
||||
private set
|
||||
|
|
@ -573,9 +573,4 @@ internal class DefaultLegacyWalletConnectRepository(
|
|||
val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } }
|
||||
return wcProvidedChains.intersect(userChains.toSet())
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
val unsupportedDApps = listOf("dYdX", "dYdX v4", "Apex Pro", "The Sandbox")
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@ import com.squareup.moshi.JsonAdapter
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.Session
|
||||
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
|
||||
import com.tangem.domain.walletconnect.model.legacy.Session
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultWalletConnectSessionsRepository(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
|||
import com.tangem.datasource.di.SdkMoshi
|
||||
import com.tangem.datasource.files.FileReader
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
|
|
@ -33,7 +34,6 @@ internal object WalletConnectInteractorModule {
|
|||
fun provideWalletConnectInteractor(
|
||||
wcRepository: LegacyWalletConnectRepository,
|
||||
wcSessionsRepository: WalletConnectSessionsRepository,
|
||||
walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
walletManagersFacade: WalletManagersFacade,
|
||||
userWalletsListManager: UserWalletsListManager,
|
||||
|
|
@ -44,7 +44,7 @@ internal object WalletConnectInteractorModule {
|
|||
walletConnectRepository = wcRepository,
|
||||
sessionsRepository = wcSessionsRepository,
|
||||
sdkHelper = WalletConnectSdkHelper(),
|
||||
blockchainHelper = TangemWcBlockchainHelper(walletConnectFeatureToggles),
|
||||
blockchainHelper = TangemWcBlockchainHelper(),
|
||||
currenciesRepository = currenciesRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
userWalletsListManager = userWalletsListManager,
|
||||
|
|
@ -69,13 +69,11 @@ internal object WalletConnectModule {
|
|||
application: Application,
|
||||
wcRequestDeserializer: WcJrpcRequestsDeserializer,
|
||||
analyticsHandler: AnalyticsEventHandler,
|
||||
walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
): LegacyWalletConnectRepository {
|
||||
return DefaultLegacyWalletConnectRepository(
|
||||
application = application,
|
||||
wcRequestDeserializer = wcRequestDeserializer,
|
||||
analyticsHandler = analyticsHandler,
|
||||
walletConnectFeatureToggles = walletConnectFeatureToggles,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.*
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction.OpenSession.SourceType
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ import com.tangem.blockchainsdk.utils.toNetworkId
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
import com.tangem.domain.walletconnect.model.legacy.Session
|
||||
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -368,10 +371,6 @@ class WalletConnectInteractor(
|
|||
}
|
||||
}
|
||||
|
||||
fun isWalletConnectUri(uri: String): Boolean {
|
||||
return uri.lowercase().startsWith(WC_SCHEME)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Wallet Connect deep links.
|
||||
* If wallet connect is able to handle the deeplink, session is started with deeplink.
|
||||
|
|
@ -436,7 +435,6 @@ class WalletConnectInteractor(
|
|||
}
|
||||
|
||||
private companion object {
|
||||
const val WC_SCHEME = "wc"
|
||||
const val WC_TOPIC_QUERY_NAME = "sessionTopic"
|
||||
const val WC_PARAM_REGEX = "([a-zA-Z\\d-]+)=([a-zA-Z\\d]+)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.Account
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
|
||||
interface WcBlockchainHelper {
|
||||
fun chainIdToNetworkIdOrNull(chainId: String): String?
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain
|
||||
|
||||
import com.tangem.domain.walletconnect.model.legacy.WalletConnectSessionsRepository
|
||||
import com.tangem.tap.domain.walletconnect.WalletConnectSdkHelper
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.BnbData
|
||||
import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.domain.walletconnect2.domain.models
|
||||
|
||||
import com.tangem.domain.walletconnect.model.legacy.Account
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WcRequest
|
||||
import java.net.URI
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,6 @@ internal class WalletConnectFeatureToggles(
|
|||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) {
|
||||
|
||||
val isSolanaTxSignEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "WC_SOLANA_TX_SIGN_ENABLED")
|
||||
val isRedesignedWalletConnectEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled("WALLET_CONNECT_REDESIGN_ENABLED")
|
||||
}
|
||||
|
|
@ -9,9 +9,7 @@ import org.rekotlin.Action
|
|||
sealed class WalletConnectAction : Action {
|
||||
data class HandleDeepLink(val wcUri: String?) : WalletConnectAction()
|
||||
|
||||
data class StartWalletConnect(
|
||||
val copiedUri: String?,
|
||||
) : WalletConnectAction()
|
||||
data object StartWalletConnect : WalletConnectAction()
|
||||
|
||||
data class OpenSession(
|
||||
val wcUri: String,
|
||||
|
|
@ -24,7 +22,6 @@ sealed class WalletConnectAction : Action {
|
|||
|
||||
data class RejectRequest(val topic: String, val id: Long) : WalletConnectAction()
|
||||
|
||||
data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction()
|
||||
//region WalletConnect 2.0
|
||||
data class ApproveProposal(val proposal: WalletConnectEvents.SessionProposal) : WalletConnectAction()
|
||||
data object RejectProposal : WalletConnectAction()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.tap.features.details.redux.walletconnect
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchainsdk.utils.toNetworkId
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
|
|
@ -57,22 +56,10 @@ class WalletConnectMiddleware {
|
|||
walletConnectInteractor.disconnectSession(action.topic)
|
||||
}
|
||||
is WalletConnectAction.StartWalletConnect -> {
|
||||
val uri = action.copiedUri
|
||||
if (uri != null && isWalletConnectUri(uri)) {
|
||||
store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri))
|
||||
} else {
|
||||
store.dispatchNavigationAction { push(AppRoute.QrScanning(SourceType.WALLET_CONNECT)) }
|
||||
store.dispatchNavigationAction {
|
||||
push(AppRoute.QrScanning(AppRoute.QrScanning.Source.WalletConnect))
|
||||
}
|
||||
}
|
||||
is WalletConnectAction.ShowClipboardOrScanQrDialog -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ClipboardOrScanQr(
|
||||
action.wcUri,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.OpenSession -> {
|
||||
val index = action.wcUri.indexOf("@")
|
||||
when (action.wcUri[index + 1]) {
|
||||
|
|
@ -197,8 +184,4 @@ class WalletConnectMiddleware {
|
|||
|
||||
return walletManagerFacade.getStoredWalletManagers(userWallet.walletId)
|
||||
}
|
||||
|
||||
private fun isWalletConnectUri(uri: String): Boolean {
|
||||
return walletConnectInteractor.isWalletConnectUri(uri)
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +83,6 @@ data class WalletForSession(
|
|||
|
||||
sealed class WalletConnectDialog : StateDialog {
|
||||
data object UnsupportedWcVersion : WalletConnectDialog()
|
||||
data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog()
|
||||
data object UnsupportedCard : WalletConnectDialog()
|
||||
data class UnsupportedNetwork(val networks: List<String>? = null) : WalletConnectDialog()
|
||||
data object UnsupportedDapp : WalletConnectDialog()
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
|
|
@ -55,6 +56,7 @@ internal class CardSettingsModel @Inject constructor(
|
|||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<CardSettingsComponent.Params>()
|
||||
|
|
@ -198,13 +200,24 @@ internal class CardSettingsModel @Inject constructor(
|
|||
}
|
||||
|
||||
if (scanResponse.cardTypesResolver.isTangemTwins()) {
|
||||
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
|
||||
if (onboardingV2FeatureToggles.isTwinRefactoringEnabled) {
|
||||
store.dispatchNavigationAction {
|
||||
push(
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = scanResponse,
|
||||
mode = AppRoute.Onboarding.Mode.RecreateWalletTwin,
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// needs to prepare twin state if it's twin cards (depends on onboarding refactoring)
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet(scanResponse)))
|
||||
|
||||
cardSettingsInteractor.clear()
|
||||
cardSettingsInteractor.clear()
|
||||
|
||||
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
|
||||
store.dispatchNavigationAction { push(AppRoute.OnboardingTwins) }
|
||||
}
|
||||
} else {
|
||||
val card = scanResponse.card
|
||||
|
||||
|
|
|
|||
|
|
@ -140,9 +140,9 @@ private fun ConditionCheckBox(checkedState: Boolean, onCheckedChange: (Boolean)
|
|||
Icon(
|
||||
painter = painterResource(
|
||||
if (checked) {
|
||||
R.drawable.ic_accepted
|
||||
R.drawable.ic_accepted_20
|
||||
} else {
|
||||
R.drawable.ic_unticked
|
||||
R.drawable.ic_unticked_20
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@ import androidx.compose.runtime.Stable
|
|||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsErrorHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Settings
|
||||
import com.tangem.tap.common.analytics.events.TangemSdkErrorEvent
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.details.ui.cardsettings.domain.CardSettingsInteractor
|
||||
|
|
@ -30,6 +32,8 @@ internal class SecurityModeModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val cardSettingsInteractor: CardSettingsInteractor,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsErrorHandler: AnalyticsErrorHandler,
|
||||
) : Model() {
|
||||
|
||||
private val scannedScanResponse = cardSettingsInteractor.scannedScanResponse.value
|
||||
|
|
@ -85,14 +89,14 @@ internal class SecurityModeModel @Inject constructor(
|
|||
val paramValue = AnalyticsParam.SecurityMode.from(selectedOption)
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue))
|
||||
analyticsEventHandler.send(Settings.CardSettings.SecurityModeChanged(paramValue))
|
||||
|
||||
store.dispatchNavigationAction(AppRouter::pop)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
val error = result.error
|
||||
if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
|
||||
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue, error))
|
||||
analyticsErrorHandler.sendErrorEvent(TangemSdkErrorEvent(error))
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import androidx.compose.runtime.Stable
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
|
|
@ -23,7 +22,6 @@ import javax.inject.Inject
|
|||
internal class WalletConnectModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
) : Model() {
|
||||
|
||||
init {
|
||||
|
|
@ -43,9 +41,7 @@ internal class WalletConnectModel @Inject constructor(
|
|||
isLoading = state.loading,
|
||||
onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) },
|
||||
onAddSession = {
|
||||
store.dispatch(
|
||||
WalletConnectAction.StartWalletConnect(copiedUri = clipboardManager.getText()),
|
||||
)
|
||||
store.dispatch(WalletConnectAction.StartWalletConnect)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.tap.features.details.ui.walletconnect.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object ClipboardOrScanQrDialog {
|
||||
fun create(wcUri: String, context: Context): AlertDialog {
|
||||
return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply {
|
||||
setTitle(context.getString(R.string.common_select_action))
|
||||
setMessage(context.getText(R.string.wallet_connect_clipboard_alert))
|
||||
setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ ->
|
||||
store.dispatch(
|
||||
WalletConnectAction.OpenSession(wcUri, WalletConnectAction.OpenSession.SourceType.CLIPBOARD),
|
||||
)
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ ->
|
||||
store.dispatchNavigationAction {
|
||||
push(AppRoute.QrScanning(source = SourceType.WALLET_CONNECT))
|
||||
}
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ import com.tangem.domain.models.scan.ScanResponse
|
|||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.tokens.TokensAction
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
|
|
@ -44,12 +43,12 @@ import javax.inject.Inject
|
|||
internal class HomeModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val scanCardProcessor: ScanCardProcessor,
|
||||
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
||||
private val saveWalletUseCase: SaveWalletUseCase,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val userWalletBuilderFactory: UserWalletBuilder.Factory,
|
||||
) : Model() {
|
||||
|
||||
private val tangemErrorHandler = TangemTangemErrorsHandler(store)
|
||||
|
|
@ -100,10 +99,7 @@ internal class HomeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
|
||||
val userWallet = UserWalletBuilder(
|
||||
scanResponse = scanResponse,
|
||||
generateWalletNameUseCase = generateWalletNameUseCase,
|
||||
).build()
|
||||
val userWallet = userWalletBuilderFactory.create(scanResponse = scanResponse).build()
|
||||
|
||||
if (userWallet == null) {
|
||||
Timber.e("User wallet not created")
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.extensions.dispatchNavigationAction
|
||||
|
|
@ -91,8 +90,9 @@ private suspend fun readCard() {
|
|||
}
|
||||
|
||||
private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch {
|
||||
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
|
||||
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build().guard {
|
||||
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
|
||||
|
||||
val userWallet = userWalletBuilder.build().guard {
|
||||
Timber.e("User wallet not created")
|
||||
return@launch
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ import com.tangem.domain.settings.usercountry.FetchUserCountryUseCase
|
|||
import com.tangem.domain.staking.FetchStakingTokensUseCase
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.feature.swap.analytics.StoriesEvents
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.features.swap.SwapFeatureToggles
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -60,8 +58,6 @@ internal class MainViewModel @Inject constructor(
|
|||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val imagePreloader: ImagePreloader,
|
||||
private val fetchHotCryptoUseCase: FetchHotCryptoUseCase,
|
||||
private val swapFeatureToggles: SwapFeatureToggles,
|
||||
onrampFeatureToggles: OnrampFeatureToggles,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : ViewModel() {
|
||||
|
||||
|
|
@ -82,9 +78,7 @@ internal class MainViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (onrampFeatureToggles.isHotTokensEnabled) {
|
||||
viewModelScope.launch { fetchHotCryptoUseCase() }
|
||||
}
|
||||
viewModelScope.launch { fetchHotCryptoUseCase() }
|
||||
|
||||
updateAppCurrencies()
|
||||
observeFlips()
|
||||
|
|
@ -130,7 +124,7 @@ internal class MainViewModel @Inject constructor(
|
|||
|
||||
private fun fetchStakingTokens() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
fetchStakingTokensUseCase(true)
|
||||
fetchStakingTokensUseCase()
|
||||
.onLeft { Timber.e(it.toString(), "Unable to fetch the staking tokens list") }
|
||||
.onRight { Timber.d("Staking token list was fetched successfully") }
|
||||
}
|
||||
|
|
@ -301,23 +295,21 @@ internal class MainViewModel @Inject constructor(
|
|||
private fun preloadImages() {
|
||||
try {
|
||||
viewModelScope.launch {
|
||||
if (swapFeatureToggles.isPromoStoriesEnabled) {
|
||||
val swapStories = getStoryContentUseCase.invokeSync(
|
||||
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
|
||||
refresh = true,
|
||||
).getOrNull()
|
||||
val swapStories = getStoryContentUseCase.invokeSync(
|
||||
id = StoryContentIds.STORY_FIRST_TIME_SWAP.id,
|
||||
refresh = true,
|
||||
).getOrNull()
|
||||
|
||||
if (swapStories == null) {
|
||||
analyticsEventHandler.send(
|
||||
StoriesEvents.Error(
|
||||
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
val storiesImages = swapStories.getImageUrls()
|
||||
// try to preload images for stories
|
||||
storiesImages.forEach(imagePreloader::preload)
|
||||
}
|
||||
if (swapStories == null) {
|
||||
analyticsEventHandler.send(
|
||||
StoriesEvents.Error(
|
||||
type = StoryContentIds.STORY_FIRST_TIME_SWAP.analyticType,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
val storiesImages = swapStories.getImageUrls()
|
||||
// try to preload images for stories
|
||||
storiesImages.forEach(imagePreloader::preload)
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.domain.models.scan.CardDTO
|
|||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
|
|
@ -70,6 +69,7 @@ object OnboardingHelper {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
|
||||
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
|
||||
scanResponse.productType == ProductType.Ring ||
|
||||
|
|
@ -85,7 +85,7 @@ object OnboardingHelper {
|
|||
scanResponse = scanResponse,
|
||||
mode = when (backupState.startedSource) {
|
||||
BackupStartedSource.Onboarding -> AppRoute.Onboarding.Mode.Onboarding
|
||||
BackupStartedSource.CreateBackup -> AppRoute.Onboarding.Mode.AddBackup
|
||||
BackupStartedSource.CreateBackup -> AppRoute.Onboarding.Mode.AddBackupWallet1
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -104,7 +104,11 @@ object OnboardingHelper {
|
|||
} else {
|
||||
AppRoute.OnboardingOther
|
||||
}
|
||||
ProductType.Twins -> AppRoute.OnboardingTwins
|
||||
ProductType.Twins -> if (featureToggles.isTwinRefactoringEnabled) {
|
||||
AppRoute.Onboarding(scanResponse = scanResponse)
|
||||
} else {
|
||||
AppRoute.OnboardingTwins
|
||||
}
|
||||
ProductType.Start2Coin,
|
||||
ProductType.Visa,
|
||||
-> throw UnsupportedOperationException("Onboarding for ${type.name} cards is not supported")
|
||||
|
|
@ -250,8 +254,9 @@ object OnboardingHelper {
|
|||
hasBackupError: Boolean,
|
||||
alreadyCreatedWallet: UserWallet? = null,
|
||||
) {
|
||||
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
|
||||
val userWallet = alreadyCreatedWallet ?: UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
|
||||
val userWalletBuilderFactory = store.inject(DaggerGraphState::userWalletBuilderFactory)
|
||||
|
||||
val userWallet = alreadyCreatedWallet ?: userWalletBuilderFactory.create(scanResponse)
|
||||
.hasBackupError(hasBackupError)
|
||||
.backupCardsIds(backupCardsIds?.toSet())
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import com.tangem.common.extensions.isZero
|
|||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.attestation.CardVerifyAndGetInfo
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.operations.attestation.api.models.CardVerifyAndGetInfo
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
|
|
@ -26,16 +26,22 @@ import java.math.BigDecimal
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Deprecated("Remove when navigation refactoring will be implemented")
|
||||
class OnboardingManager(var scanResponse: ScanResponse) {
|
||||
|
||||
private val cardRepository: CardRepository = store.inject(DaggerGraphState::cardRepository)
|
||||
private val cardRepository: CardRepository by lazy { store.inject(DaggerGraphState::cardRepository) }
|
||||
|
||||
private val onlineCardVerifier: OnlineCardVerifier by lazy { store.inject(DaggerGraphState::onlineCardVerifier) }
|
||||
|
||||
private var cardInfo: Result<CardVerifyAndGetInfo.Response.Item>? = null
|
||||
|
||||
suspend fun loadArtworkUrl(): String {
|
||||
val cardInfo = cardInfo
|
||||
?: OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
|
||||
?: onlineCardVerifier.getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey)
|
||||
|
||||
this.cardInfo = cardInfo
|
||||
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)
|
||||
|
||||
return scanResponse.card.getOrLoadCardArtworkUrl(cardInfo, onlineCardVerifier)
|
||||
}
|
||||
|
||||
suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Deprecated("Use OnboardingNoteComponent")
|
||||
class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
|
||||
|
||||
private var previousStep: OnboardingNoteStep = OnboardingNoteStep.None
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
|
|
@ -12,6 +11,7 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.common.services.Result
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.ScreensSources
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.toWrappedList
|
||||
|
|
@ -24,7 +24,6 @@ import com.tangem.domain.models.scan.CardDTO
|
|||
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
|
||||
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.Artwork
|
||||
|
|
@ -32,7 +31,7 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
|
||||
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.BackupValidator
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.operations.attestation.CardArtworksProvider
|
||||
import com.tangem.operations.backup.BackupService
|
||||
import com.tangem.sdk.api.CreateProductWalletTaskResponse
|
||||
import com.tangem.sdk.extensions.localizedDescriptionRes
|
||||
|
|
@ -243,13 +242,15 @@ private suspend fun readCard(): CompletionResult<ScanResponse> {
|
|||
}
|
||||
|
||||
private suspend fun loadArtworkForCard(cardId: String, cardPublicKey: ByteArray, defaultArtwork: Uri?): Uri {
|
||||
return when (val cardInfo = OnlineCardVerifier().getCardInfo(cardId, cardPublicKey)) {
|
||||
val onlineCardVerifier = store.inject(DaggerGraphState::onlineCardVerifier)
|
||||
|
||||
return when (val cardInfo = onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
|
||||
is Result.Success -> {
|
||||
val artworkId = cardInfo.data.artwork?.id
|
||||
if (artworkId.isNullOrEmpty()) {
|
||||
defaultArtwork ?: Uri.EMPTY
|
||||
} else {
|
||||
Uri.parse(OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId))
|
||||
Uri.parse(CardArtworksProvider.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId))
|
||||
}
|
||||
}
|
||||
is Result.Failure -> defaultArtwork ?: Uri.EMPTY
|
||||
|
|
@ -453,8 +454,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
store.dispatchOnMain(BackupAction.AddBackupCard.Success(result.data))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
val crashlytics = FirebaseCrashlytics.getInstance()
|
||||
|
||||
when (val error = result.error) {
|
||||
is TangemSdkError.CardVerificationFailed -> {
|
||||
Analytics.send(
|
||||
|
|
@ -491,7 +490,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
GlobalAction.ShowDialog(BackupDialog.AttestationFailed),
|
||||
)
|
||||
}
|
||||
else -> crashlytics.recordException(error)
|
||||
else -> Analytics.sendException(ExceptionAnalyticsEvent(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -787,9 +786,10 @@ private suspend fun updateWallet(
|
|||
}
|
||||
|
||||
private suspend fun createUserWallet(scanResponse: ScanResponse, backupState: BackupState): UserWallet {
|
||||
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
|
||||
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
|
||||
|
||||
return requireNotNull(
|
||||
value = UserWalletBuilder(scanResponse, walletNameGenerateUseCase)
|
||||
value = userWalletBuilder
|
||||
.backupCardsIds(backupState.backupCardIds.toSet())
|
||||
.hasBackupError(backupState.hasBackupError)
|
||||
.build(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import androidx.appcompat.app.AlertDialog
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
|
|
@ -70,8 +69,10 @@ internal class SaveWalletMiddleware {
|
|||
scope.launch {
|
||||
val backupInfo = state.backupInfo ?: error("Backup info is null")
|
||||
|
||||
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
|
||||
val userWallet = UserWalletBuilder(backupInfo.scanResponse, walletNameGenerateUseCase)
|
||||
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory)
|
||||
.create(scanResponse = backupInfo.scanResponse)
|
||||
|
||||
val userWallet = userWalletBuilder
|
||||
.backupCardsIds(state.backupInfo.backupCardsIds)
|
||||
.hasBackupError(hasBackupError)
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import com.tangem.common.routing.utils.popTo
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
|
||||
import com.tangem.domain.wallets.legacy.unlockIfLockable
|
||||
import com.tangem.tap.backupService
|
||||
|
|
@ -119,9 +117,9 @@ internal class WelcomeMiddleware {
|
|||
)
|
||||
|
||||
scanCardInternal { scanResponse ->
|
||||
val walletNameGenerateUseCase = store.inject(DaggerGraphState::generateWalletNameUseCase)
|
||||
val userWallet = UserWalletBuilder(scanResponse, walletNameGenerateUseCase).build()
|
||||
?: return@scanCardInternal
|
||||
val userWalletBuilder = store.inject(DaggerGraphState::userWalletBuilderFactory).create(scanResponse)
|
||||
|
||||
val userWallet = userWalletBuilder.build() ?: return@scanCardInternal
|
||||
|
||||
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
userWalletsListManager.save(userWallet, canOverride = true)
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
|||
import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRACT_ADDRESS_VALUE
|
||||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.exchange.ExchangeableState
|
||||
import com.tangem.domain.exchange.ExpressAvailabilityState
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
|
|
@ -27,7 +26,6 @@ import com.tangem.utils.coroutines.runCatching
|
|||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class DefaultRampManager(
|
||||
|
|
@ -39,7 +37,6 @@ internal class DefaultRampManager(
|
|||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val onrampFeatureToggles: OnrampFeatureToggles,
|
||||
private val expressAssetsStore: ExpressAssetsStore,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : RampStateManager {
|
||||
|
||||
|
|
@ -49,19 +46,12 @@ internal class DefaultRampManager(
|
|||
scanResponse: ScanResponse,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): Boolean {
|
||||
return runCatching {
|
||||
when {
|
||||
onrampFeatureToggles.isFeatureEnabled -> getOnrampAvailable(userWalletId, cryptoCurrency)
|
||||
exchangeService != null -> exchangeService.availableForBuy(
|
||||
scanResponse = scanResponse,
|
||||
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
|
||||
)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
.getOrNull()
|
||||
?: false
|
||||
): ScenarioUnavailabilityReason {
|
||||
val availabilityState = runCatching {
|
||||
getOnrampAvailableState(scanResponse, userWalletId, cryptoCurrency)
|
||||
}.getOrNull() ?: ExpressAvailabilityState.Error
|
||||
|
||||
return availabilityState.toReason(cryptoCurrency.name)
|
||||
}
|
||||
|
||||
override suspend fun availableForSell(
|
||||
|
|
@ -103,8 +93,11 @@ internal class DefaultRampManager(
|
|||
override suspend fun availableForSwap(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExchangeableState {
|
||||
return runCatching { getExchangeableState(userWalletId, cryptoCurrency) }.getOrNull() ?: ExchangeableState.Error
|
||||
): ScenarioUnavailabilityReason {
|
||||
val availabilityState = runCatching {
|
||||
getExchangeableState(userWalletId, cryptoCurrency)
|
||||
}.getOrNull() ?: ExpressAvailabilityState.Error
|
||||
return availabilityState.toReason(cryptoCurrency.name)
|
||||
}
|
||||
|
||||
override fun getBuyInitializationStatus(): Flow<ExchangeServiceInitializationStatus> {
|
||||
|
|
@ -134,33 +127,76 @@ internal class DefaultRampManager(
|
|||
private suspend fun getExchangeableState(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExchangeableState {
|
||||
return withContext(dispatchers.io) {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return@withContext ExchangeableState.Loading
|
||||
when (asset) {
|
||||
is Lce.Error -> ExchangeableState.Error
|
||||
is Lce.Loading -> ExchangeableState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.exchangeAvailable?.toExchangeableState() ?: ExchangeableState.AssetNotFound
|
||||
}
|
||||
): ExpressAvailabilityState {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return ExpressAvailabilityState.Loading
|
||||
return when (asset) {
|
||||
is Lce.Error -> ExpressAvailabilityState.Error
|
||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.exchangeAvailable?.toSwapAvailabilityState()
|
||||
?: ExpressAvailabilityState.AssetNotFound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Boolean.toExchangeableState(): ExchangeableState {
|
||||
return if (this) {
|
||||
ExchangeableState.Exchangeable
|
||||
} else {
|
||||
ExchangeableState.NotExchangeable
|
||||
private suspend fun getOnrampAvailableState(
|
||||
scanResponse: ScanResponse,
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExpressAvailabilityState {
|
||||
return when {
|
||||
onrampFeatureToggles.isFeatureEnabled -> {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return ExpressAvailabilityState.Loading
|
||||
return when (asset) {
|
||||
is Lce.Error -> ExpressAvailabilityState.Error
|
||||
is Lce.Loading -> ExpressAvailabilityState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.onrampAvailable?.toOnrampAvailabilityState()
|
||||
?: ExpressAvailabilityState.AssetNotFound
|
||||
}
|
||||
}
|
||||
}
|
||||
exchangeService != null -> {
|
||||
exchangeService.availableForBuy(
|
||||
scanResponse = scanResponse,
|
||||
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
|
||||
).toOnrampAvailabilityState()
|
||||
}
|
||||
else -> ExpressAvailabilityState.Error
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getOnrampAvailable(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
val asset = expressAssetsStore.getSyncOrNull(userWalletId)?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
private fun ExpressAvailabilityState.toReason(currencyName: String): ScenarioUnavailabilityReason {
|
||||
return when (this) {
|
||||
ExpressAvailabilityState.Available -> {
|
||||
ScenarioUnavailabilityReason.None
|
||||
}
|
||||
ExpressAvailabilityState.AssetNotFound -> ScenarioUnavailabilityReason.AssetNotFound(currencyName)
|
||||
ExpressAvailabilityState.Error -> ScenarioUnavailabilityReason.ExpressUnreachable(currencyName)
|
||||
ExpressAvailabilityState.Loading -> ScenarioUnavailabilityReason.ExpressLoading(currencyName)
|
||||
ExpressAvailabilityState.NotOnrampable -> ScenarioUnavailabilityReason.BuyUnavailable(currencyName)
|
||||
ExpressAvailabilityState.NotExchangeable -> ScenarioUnavailabilityReason.NotExchangeable(currencyName)
|
||||
}
|
||||
}
|
||||
|
||||
return asset?.onrampAvailable ?: false
|
||||
private fun Boolean.toSwapAvailabilityState(): ExpressAvailabilityState {
|
||||
return if (this) {
|
||||
ExpressAvailabilityState.Available
|
||||
} else {
|
||||
ExpressAvailabilityState.NotExchangeable
|
||||
}
|
||||
}
|
||||
|
||||
private fun Boolean.toOnrampAvailabilityState(): ExpressAvailabilityState {
|
||||
return if (this) {
|
||||
ExpressAvailabilityState.Available
|
||||
} else {
|
||||
ExpressAvailabilityState.NotOnrampable
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.findAssetPredicate(asset: Asset): Boolean {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512
|
|||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.data.onramp.legacy.mercuryoNetwork
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
|
|
|
|||
|
|
@ -157,4 +157,5 @@ internal val Blockchain.moonPaySupportedCurrency: MoonPaySupportedCurrency?
|
|||
Scroll, ScrollTestnet -> null
|
||||
ZkLinkNova, ZkLinkNovaTestnet -> null
|
||||
KaspaTestnet -> null
|
||||
Pepecoin, PepecoinTestnet -> null
|
||||
}
|
||||
|
|
@ -26,13 +26,14 @@ import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
|||
import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onboarding.repository.OnboardingRepository
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.operations.attestation.CardArtworksProvider
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles
|
||||
import com.tangem.tap.domain.walletconnect2.domain.LegacyWalletConnectRepository
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
|
|
@ -56,7 +57,6 @@ data class DaggerGraphState(
|
|||
val generalUserWalletsListManager: UserWalletsListManager? = null,
|
||||
val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null,
|
||||
val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null,
|
||||
val generateWalletNameUseCase: GenerateWalletNameUseCase? = null,
|
||||
val cardRepository: CardRepository? = null,
|
||||
val settingsRepository: SettingsRepository? = null,
|
||||
val blockchainSDKFactory: BlockchainSDKFactory? = null,
|
||||
|
|
@ -67,7 +67,6 @@ data class DaggerGraphState(
|
|||
val shareManager: ShareManager? = null,
|
||||
val appRouter: AppRouter? = null,
|
||||
val transactionSignerFactory: TransactionSignerFactory? = null,
|
||||
val getUserCountryUseCase: GetUserCountryUseCase? = null,
|
||||
val onrampFeatureToggles: OnrampFeatureToggles? = null,
|
||||
val environmentConfigStorage: EnvironmentConfigStorage? = null,
|
||||
val onboardingV2FeatureToggles: OnboardingV2FeatureToggles? = null,
|
||||
|
|
@ -77,4 +76,7 @@ data class DaggerGraphState(
|
|||
val clipboardManager: ClipboardManager? = null,
|
||||
val settingsManager: SettingsManager? = null,
|
||||
val uiMessageSender: UiMessageSender? = null,
|
||||
val onlineCardVerifier: OnlineCardVerifier? = null,
|
||||
val cardArworksProvider: CardArtworksProvider? = null,
|
||||
val userWalletBuilderFactory: UserWalletBuilder.Factory? = null,
|
||||
) : StateType
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package com.tangem.tap.routing
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.tap.routing.configurator.AppRouterConfig
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -15,6 +16,7 @@ import kotlin.reflect.KClass
|
|||
internal class ProxyAppRouter(
|
||||
private val config: AppRouterConfig,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : AppRouter {
|
||||
|
||||
private val routerScope: CoroutineScope
|
||||
|
|
@ -76,7 +78,7 @@ internal class ProxyAppRouter(
|
|||
|
||||
override fun defaultCompletionHandler(isSuccess: Boolean, errorMessage: String) {
|
||||
if (!isSuccess) {
|
||||
FirebaseCrashlytics.getInstance().recordException(RuntimeException(errorMessage))
|
||||
analyticsExceptionHandler.sendException(ExceptionAnalyticsEvent(RuntimeException(errorMessage)))
|
||||
Timber.w(errorMessage)
|
||||
|
||||
with(receiver = config.snackbarHandler ?: return) {
|
||||
|
|
|
|||
|
|
@ -2,28 +2,37 @@ package com.tangem.tap.routing
|
|||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.*
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.ui.UiDependencies
|
||||
import com.tangem.core.ui.components.snackbar.TangemSnackbarHost
|
||||
import com.tangem.core.ui.message.EventMessageEffect
|
||||
import com.tangem.core.ui.res.LocalIsNavigationRefactoringEnabled
|
||||
import com.tangem.core.ui.res.LocalSnackbarHostState
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.WindowInsetsZero
|
||||
import com.tangem.tap.routing.component.RoutingComponent
|
||||
|
||||
@OptIn(ExperimentalDecomposeApi::class)
|
||||
@Composable
|
||||
internal fun RootContent(
|
||||
stack: ChildStack<AppRoute, RoutingComponent.Child>,
|
||||
stack: Value<ChildStack<AppRoute, RoutingComponent.Child>>,
|
||||
uiDependencies: UiDependencies,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -33,21 +42,15 @@ internal fun RootContent(
|
|||
activity = context as Activity,
|
||||
uiDependencies = uiDependencies,
|
||||
) {
|
||||
val snackbarHostState = LocalSnackbarHostState.current
|
||||
CompositionLocalProvider(
|
||||
LocalIsNavigationRefactoringEnabled provides true,
|
||||
) {
|
||||
val snackbarHostState = LocalSnackbarHostState.current
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
snackbarHost = {
|
||||
TangemSnackbarHost(
|
||||
modifier = Modifier.padding(all = 16.dp),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
},
|
||||
contentWindowInsets = WindowInsetsZero,
|
||||
content = { paddingValues ->
|
||||
Box(Modifier.background(TangemTheme.colors.background.primary)) {
|
||||
Children(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
modifier = modifier,
|
||||
animation = stackAnimation(fade()),
|
||||
stack = stack,
|
||||
) { child ->
|
||||
when (val instance = child.instance) {
|
||||
|
|
@ -57,14 +60,24 @@ internal fun RootContent(
|
|||
}
|
||||
is RoutingComponent.Child.LegacyIntent -> {
|
||||
// TODO: Remove and use it's own router: [REDACTED_JIRA]
|
||||
startActivity(context, instance.intent, Bundle.EMPTY)
|
||||
LaunchedEffect(instance) {
|
||||
startActivity(context, instance.intent, Bundle.EMPTY)
|
||||
}
|
||||
}
|
||||
is RoutingComponent.Child.LegacyFragment,
|
||||
-> error("Unsupported child: $instance")
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
TangemSnackbarHost(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.navigationBarsPadding()
|
||||
.padding(all = 16.dp),
|
||||
hostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
EventMessageEffect()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.subscribe
|
||||
import com.arkivanov.essenty.backhandler.BackCallback
|
||||
import com.arkivanov.essenty.lifecycle.doOnDestroy
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.common.routing.AppRoute
|
||||
|
|
@ -43,19 +41,15 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
AppComponentContext by context,
|
||||
SnackbarHandler {
|
||||
|
||||
private val backCallback = BackCallback(priority = Int.MIN_VALUE, onBack = router::pop)
|
||||
|
||||
override val stack: Value<ChildStack<AppRoute, Child>> = childStack(
|
||||
source = navigationProvider.getOrCreateTyped(),
|
||||
initialStack = { getInitialStackOrInit() },
|
||||
serializer = null, // AppRoute.serializer(), // Disabled until Nav refactoring completes
|
||||
handleBackButton = false,
|
||||
handleBackButton = routingFeatureToggles.isNavigationRefactoringEnabled,
|
||||
childFactory = ::child,
|
||||
)
|
||||
|
||||
init {
|
||||
backHandler.register(backCallback)
|
||||
|
||||
lifecycle.doOnDestroy {
|
||||
childFactory.doOnDestroy()
|
||||
}
|
||||
|
|
@ -77,8 +71,6 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stack by this.stack.subscribeAsState()
|
||||
|
||||
RootContent(
|
||||
modifier = modifier,
|
||||
stack = stack,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.routing.utils
|
|||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.feature.qrscanning.QrScanningComponent
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.stories.api.StoriesComponent
|
||||
|
|
@ -11,15 +12,22 @@ import com.tangem.features.disclaimer.api.components.DisclaimerComponent
|
|||
import com.tangem.features.managetokens.component.ManageTokensComponent
|
||||
import com.tangem.features.managetokens.component.ManageTokensSource
|
||||
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.nft.component.NFTCollectionsComponent
|
||||
import com.tangem.features.nft.component.NFTDetailsComponent
|
||||
import com.tangem.features.nft.component.NFTReceiveComponent
|
||||
import com.tangem.features.nft.component.NFTAssetTraitsComponent
|
||||
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
|
||||
import com.tangem.features.onramp.component.*
|
||||
import com.tangem.features.pushnotifications.api.PushNotificationsComponent
|
||||
import com.tangem.features.send.api.SendComponent
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.staking.api.StakingComponent
|
||||
import com.tangem.features.swap.SwapComponent
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
import com.tangem.tap.domain.walletconnect2.toggles.WalletConnectFeatureToggles
|
||||
import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent
|
||||
import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent
|
||||
import com.tangem.tap.features.details.ui.cardsettings.api.CardSettingsComponent
|
||||
|
|
@ -40,6 +48,7 @@ import com.tangem.utils.Provider
|
|||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import java.util.WeakHashMap
|
||||
import javax.inject.Inject
|
||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent as RedisegnedWalletConnectComponent
|
||||
|
||||
@ActivityScoped
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -73,37 +82,43 @@ internal class ChildFactory @Inject constructor(
|
|||
private val referralComponentFactory: ReferralComponent.Factory,
|
||||
private val pushNotificationsComponentFactory: PushNotificationsComponent.Factory,
|
||||
private val walletComponentFactory: WalletEntryComponent.Factory,
|
||||
private val sendComponentFactoryV2: com.tangem.features.send.v2.api.SendComponent.Factory,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val redesignedWalletConnectComponentFactory: RedisegnedWalletConnectComponent.Factory,
|
||||
private val nftCollectionsComponentFactory: NFTCollectionsComponent.Factory,
|
||||
private val nftReceiveComponentFactory: NFTReceiveComponent.Factory,
|
||||
private val nftDetailsComponentFactory: NFTDetailsComponent.Factory,
|
||||
private val nftAssetTraitsComponentFactory: NFTAssetTraitsComponent.Factory,
|
||||
private val nftSendComponentFactory: NFTSendComponent.Factory,
|
||||
private val testerRouter: TesterRouter,
|
||||
private val routingFeatureToggles: RoutingFeatureToggles,
|
||||
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
|
||||
) {
|
||||
|
||||
fun createChild(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
||||
return if (routingFeatureToggles.isNavigationRefactoringEnabled) {
|
||||
createChildNew(route, contextFactory)
|
||||
createChildNew(route, contextFactory(route))
|
||||
} else {
|
||||
createChildLegacy(route, contextFactory)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
private fun createChildNew(route: AppRoute, contextFactory: (route: AppRoute) -> AppComponentContext): Child {
|
||||
componentContexts[route] = contextFactory(route)
|
||||
|
||||
// region Child creation
|
||||
private fun createChildNew(route: AppRoute, context: AppComponentContext): Child {
|
||||
return when (route) {
|
||||
is AppRoute.Initial -> {
|
||||
Child.Initial
|
||||
}
|
||||
is AppRoute.Details -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = DetailsComponent.Params(route.userWalletId),
|
||||
componentFactory = detailsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Disclaimer -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = DisclaimerComponent.Params(route.isTosAccepted),
|
||||
componentFactory = disclaimerComponentFactory,
|
||||
)
|
||||
|
|
@ -116,14 +131,14 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = ManageTokensComponent.Params(route.userWalletId, source),
|
||||
componentFactory = manageTokensComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Welcome -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = WelcomeComponent.Params(
|
||||
intent = route.intent,
|
||||
),
|
||||
|
|
@ -135,14 +150,14 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.WalletSettings -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = WalletSettingsComponent.Params(route.userWalletId),
|
||||
componentFactory = walletSettingsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.MarketsTokenDetails -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = MarketsTokenDetailsComponent.Params(
|
||||
token = route.token,
|
||||
appCurrency = route.appCurrency,
|
||||
|
|
@ -159,7 +174,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.Onramp -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = OnrampComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
cryptoCurrency = route.currency,
|
||||
|
|
@ -170,42 +185,45 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.OnrampSuccess -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = OnrampSuccessComponent.Params(route.externalTxId),
|
||||
componentFactory = onrampSuccessComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.BuyCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = BuyCryptoComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = buyCryptoComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SellCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = SellCryptoComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = sellCryptoComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.SwapCrypto -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = SwapSelectTokensComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = swapSelectTokensComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Onboarding -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = OnboardingEntryComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
multiWalletMode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
|
||||
mode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.Mode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackupWallet1 -> OnboardingEntryComponent.Mode.AddBackupWallet1
|
||||
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
|
||||
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
|
||||
OnboardingEntryComponent.Mode.RecreateWalletTwin
|
||||
AppRoute.Onboarding.Mode.ContinueFinalize ->
|
||||
OnboardingEntryComponent.MultiWalletMode.ContinueFinalize
|
||||
OnboardingEntryComponent.Mode.ContinueFinalize
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
|
|
@ -213,7 +231,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.Stories -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = StoriesComponent.Params(
|
||||
storyId = route.storyId,
|
||||
nextScreen = route.nextScreen,
|
||||
|
|
@ -224,7 +242,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.CurrencyDetails -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = TokenDetailsComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
|
|
@ -234,7 +252,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.Staking -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = StakingComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
cryptoCurrencyId = route.cryptoCurrencyId,
|
||||
|
|
@ -245,7 +263,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.Swap -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = SwapComponent.Params(
|
||||
currencyFrom = route.currencyFrom,
|
||||
currencyTo = route.currencyTo,
|
||||
|
|
@ -257,74 +275,101 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.Send -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactory,
|
||||
)
|
||||
if (sendFeatureToggles.isSendV2Enabled) {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = com.tangem.features.send.v2.api.SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactoryV2,
|
||||
)
|
||||
} else {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.Home -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = homeComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.WalletConnectSessions -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = redesignedWalletConnectComponentFactory,
|
||||
)
|
||||
} else {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.QrScanning -> {
|
||||
val source = when (route.source) {
|
||||
is AppRoute.QrScanning.Source.Send -> SourceType.SEND
|
||||
is AppRoute.QrScanning.Source.WalletConnect -> SourceType.WALLET_CONNECT
|
||||
}
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = QrScanningComponent.Params(
|
||||
source = route.source,
|
||||
networkName = route.networkName,
|
||||
source = source,
|
||||
networkName = (route.source as? AppRoute.QrScanning.Source.Send)?.networkName,
|
||||
),
|
||||
componentFactory = qrScanningComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.AccessCodeRecovery -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = accessCodeRecoveryComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.CardSettings -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = CardSettingsComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = cardSettingsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.AppCurrencySelector -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = appCurrencySelectorComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.AppSettings -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = appSettingsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.DetailsSecurity -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = SecurityModeComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
),
|
||||
|
|
@ -333,7 +378,7 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.ResetToFactory -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = ResetCardComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
cardId = route.cardId,
|
||||
|
|
@ -345,25 +390,64 @@ internal class ChildFactory @Inject constructor(
|
|||
}
|
||||
is AppRoute.ReferralProgram -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = ReferralComponent.Params(route.userWalletId),
|
||||
componentFactory = referralComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.PushNotification -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = pushNotificationsComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.Wallet -> {
|
||||
createComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
context = context,
|
||||
params = Unit,
|
||||
componentFactory = walletComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.NFTCollections ->
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NFTCollectionsComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = nftCollectionsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTReceive ->
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = nftReceiveComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTDetails ->
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NFTDetailsComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
nftAsset = route.nftAsset,
|
||||
nftCollectionName = route.collectionName,
|
||||
),
|
||||
componentFactory = nftDetailsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTAssetTraits ->
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NFTAssetTraitsComponent.Params(nftAsset = route.nftAsset),
|
||||
componentFactory = nftAssetTraitsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTSend -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = NFTSendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
nftAsset = route.nftAsset,
|
||||
nftCollectionName = route.nftCollectionName,
|
||||
),
|
||||
componentFactory = nftSendComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.OnboardingNote,
|
||||
is AppRoute.SaveWallet,
|
||||
is AppRoute.OnboardingOther,
|
||||
|
|
@ -401,18 +485,33 @@ internal class ChildFactory @Inject constructor(
|
|||
route.asFragmentChild(Provider { SaveWalletBottomSheetFragment() })
|
||||
}
|
||||
is AppRoute.Send -> {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactory,
|
||||
)
|
||||
if (sendFeatureToggles.isSendV2Enabled) {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = com.tangem.features.send.v2.api.SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactoryV2,
|
||||
)
|
||||
} else {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = SendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
currency = route.currency,
|
||||
transactionId = route.transactionId,
|
||||
amount = route.amount,
|
||||
tag = route.tag,
|
||||
destinationAddress = route.destinationAddress,
|
||||
),
|
||||
componentFactory = sendComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.AppSettings -> {
|
||||
route.asComponentChild(
|
||||
|
|
@ -484,11 +583,15 @@ internal class ChildFactory @Inject constructor(
|
|||
route.asFragmentChild(Provider { OnboardingWalletFragment() })
|
||||
}
|
||||
is AppRoute.QrScanning -> {
|
||||
val source = when (route.source) {
|
||||
is AppRoute.QrScanning.Source.Send -> SourceType.SEND
|
||||
is AppRoute.QrScanning.Source.WalletConnect -> SourceType.WALLET_CONNECT
|
||||
}
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = QrScanningComponent.Params(
|
||||
source = route.source,
|
||||
networkName = route.networkName,
|
||||
source = source,
|
||||
networkName = (route.source as? AppRoute.QrScanning.Source.Send)?.networkName,
|
||||
),
|
||||
componentFactory = qrScanningComponentFactory,
|
||||
)
|
||||
|
|
@ -533,11 +636,19 @@ internal class ChildFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
is AppRoute.WalletConnectSessions -> {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
if (walletConnectFeatureToggles.isRedesignedWalletConnectEnabled) {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = redesignedWalletConnectComponentFactory,
|
||||
)
|
||||
} else {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = Unit,
|
||||
componentFactory = walletConnectComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
is AppRoute.CurrencyDetails -> {
|
||||
route.asComponentChild(
|
||||
|
|
@ -647,11 +758,14 @@ internal class ChildFactory @Inject constructor(
|
|||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = OnboardingEntryComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
multiWalletMode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
|
||||
mode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.Mode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackupWallet1 -> OnboardingEntryComponent.Mode.AddBackupWallet1
|
||||
AppRoute.Onboarding.Mode.WelcomeOnlyTwin -> OnboardingEntryComponent.Mode.WelcomeOnlyTwin
|
||||
AppRoute.Onboarding.Mode.RecreateWalletTwin ->
|
||||
OnboardingEntryComponent.Mode.RecreateWalletTwin
|
||||
AppRoute.Onboarding.Mode.ContinueFinalize ->
|
||||
OnboardingEntryComponent.MultiWalletMode.ContinueFinalize
|
||||
OnboardingEntryComponent.Mode.ContinueFinalize
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
|
|
@ -668,6 +782,45 @@ internal class ChildFactory @Inject constructor(
|
|||
componentFactory = storiesComponentFactory,
|
||||
)
|
||||
}
|
||||
is AppRoute.NFTCollections ->
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = NFTCollectionsComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = nftCollectionsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTReceive ->
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = NFTReceiveComponent.Params(userWalletId = route.userWalletId),
|
||||
componentFactory = nftReceiveComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTDetails ->
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = NFTDetailsComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
nftAsset = route.nftAsset,
|
||||
nftCollectionName = route.collectionName,
|
||||
),
|
||||
componentFactory = nftDetailsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTAssetTraits ->
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = NFTAssetTraitsComponent.Params(nftAsset = route.nftAsset),
|
||||
componentFactory = nftAssetTraitsComponentFactory,
|
||||
)
|
||||
is AppRoute.NFTSend -> {
|
||||
route.asComponentChild(
|
||||
contextProvider = contextProvider(route, contextFactory),
|
||||
params = NFTSendComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
nftAsset = route.nftAsset,
|
||||
nftCollectionName = route.nftCollectionName,
|
||||
),
|
||||
componentFactory = nftSendComponentFactory,
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,11 +40,9 @@ internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C
|
|||
}
|
||||
|
||||
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>> createComponentChild(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
context: AppComponentContext,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
): Child {
|
||||
val context = contextProvider()
|
||||
|
||||
return Child.ComposableComponent(componentFactory.create(context, params))
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/access_code_info_protect_margin_top"
|
||||
android:src="@drawable/ic_access_code"
|
||||
android:src="@drawable/ic_access_code_119"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import android.annotation.SuppressLint
|
|||
import com.google.common.truth.Truth
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.card.ScanCardException
|
||||
import com.tangem.domain.common.configs.GenericCardConfig
|
||||
|
|
@ -37,10 +39,9 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
private val defaultUserWallet = UserWallet(
|
||||
name = "",
|
||||
walletId = defaultUserWalletId,
|
||||
artworkUrl = "",
|
||||
cardsInWallet = setOf(),
|
||||
isMultiCurrency = false,
|
||||
scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
|
||||
scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
|
||||
hasBackupError = false,
|
||||
)
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
@Test
|
||||
fun `success if currencies is empty`() = runTest {
|
||||
val userWallet = defaultUserWallet.copy(
|
||||
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
)
|
||||
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
@Test
|
||||
fun `success if card already has derivations`() = runTest {
|
||||
val userWallet = defaultUserWallet.copy(
|
||||
scanResponse = ScanResponseMockFactory.create(
|
||||
scanResponse = MockScanResponseFactory.create(
|
||||
cardConfig = MultiWalletCardConfig,
|
||||
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
|
||||
),
|
||||
|
|
@ -105,7 +106,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
runCatching {
|
||||
repository.derivePublicKeys(
|
||||
userWalletId = defaultUserWalletId,
|
||||
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
|
||||
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
|
||||
)
|
||||
}
|
||||
.onSuccess { Truth.assertThat(it) }
|
||||
|
|
@ -119,7 +120,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
@Test
|
||||
fun `error if tangemSdkManager throws exception`() = runTest {
|
||||
val userWallet = defaultUserWallet.copy(
|
||||
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
)
|
||||
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
|
||||
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } throws ScanCardException.UserCancelled
|
||||
|
|
@ -127,7 +128,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
runCatching {
|
||||
repository.derivePublicKeys(
|
||||
userWalletId = defaultUserWalletId,
|
||||
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
|
||||
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
|
||||
)
|
||||
}
|
||||
.onSuccess { error("Should throws exception") }
|
||||
|
|
@ -142,7 +143,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
@Test
|
||||
fun `success case`() = runTest {
|
||||
val userWallet = defaultUserWallet.copy(
|
||||
scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap()),
|
||||
)
|
||||
coEvery { userWalletsStore.getSyncOrNull(defaultUserWalletId) } returns userWallet
|
||||
coEvery { tangemSdkManager.derivePublicKeys(null, any(), any()) } returns CompletionResult.Success(
|
||||
|
|
@ -153,7 +154,7 @@ internal class DefaultDerivationsRepositoryTest {
|
|||
runCatching {
|
||||
repository.derivePublicKeys(
|
||||
userWalletId = defaultUserWalletId,
|
||||
currencies = CryptoCurrenciesMocks(userWallet.scanResponse).ethereum,
|
||||
currencies = MockCryptoCurrencyFactory(userWallet.scanResponse).ethereum.let(::listOf),
|
||||
)
|
||||
}
|
||||
.onSuccess { Truth.assertThat(it) }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.derivation.DerivationConfigV2
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.common.configs.GenericCardConfig
|
||||
import com.tangem.domain.common.configs.MultiWalletCardConfig
|
||||
|
|
@ -20,7 +22,7 @@ internal class MissedDerivationsFinderTest {
|
|||
|
||||
@Test
|
||||
fun `empty derivations for empty currencies`() {
|
||||
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
|
||||
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val actual = finder.find(emptyList())
|
||||
|
|
@ -31,10 +33,10 @@ internal class MissedDerivationsFinderTest {
|
|||
@Test
|
||||
fun `empty derivations for non supported blockchains`() {
|
||||
// Bls is not supported
|
||||
val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
|
||||
val scanResponse = MockScanResponseFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).chia
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).chia.let(::listOf)
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).isEmpty()
|
||||
|
|
@ -43,7 +45,7 @@ internal class MissedDerivationsFinderTest {
|
|||
@Test
|
||||
fun `derivations ONLY for supported blockchains`() {
|
||||
// Bls is not supported
|
||||
val scanResponse = ScanResponseMockFactory.create(
|
||||
val scanResponse = MockScanResponseFactory.create(
|
||||
cardConfig = GenericCardConfig(2),
|
||||
derivedKeys = emptyMap(),
|
||||
).let {
|
||||
|
|
@ -55,7 +57,7 @@ internal class MissedDerivationsFinderTest {
|
|||
}
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).chiaAndEthereum
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).chiaAndEthereum
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
|
|
@ -66,10 +68,10 @@ internal class MissedDerivationsFinderTest {
|
|||
|
||||
@Test
|
||||
fun `derivations for custom token`() {
|
||||
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
|
||||
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumTokenWithBinanceDerivation
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumTokenWithBinanceDerivation
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
|
|
@ -83,10 +85,10 @@ internal class MissedDerivationsFinderTest {
|
|||
|
||||
@Test
|
||||
fun `derivations for cardano`() {
|
||||
val scanResponse = ScanResponseMockFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
|
||||
val scanResponse = MockScanResponseFactory.create(cardConfig = MultiWalletCardConfig, derivedKeys = emptyMap())
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).cardano
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).cardano.let(::listOf)
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
|
|
@ -105,13 +107,13 @@ internal class MissedDerivationsFinderTest {
|
|||
|
||||
@Test
|
||||
fun `empty derivations for already derived currencies`() {
|
||||
val scanResponse = ScanResponseMockFactory.create(
|
||||
val scanResponse = MockScanResponseFactory.create(
|
||||
cardConfig = Wallet2CardConfig,
|
||||
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
|
||||
)
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).ethereum
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereum.let(::listOf)
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).isEmpty()
|
||||
|
|
@ -119,13 +121,13 @@ internal class MissedDerivationsFinderTest {
|
|||
|
||||
@Test
|
||||
fun `derivations ONLY for never derived currencies`() {
|
||||
val scanResponse = ScanResponseMockFactory.create(
|
||||
val scanResponse = MockScanResponseFactory.create(
|
||||
cardConfig = MultiWalletCardConfig,
|
||||
derivedKeys = DerivedKeysMocks.ethereumDerivedKeys,
|
||||
)
|
||||
val finder = MissedDerivationsFinder(scanResponse)
|
||||
|
||||
val currencies = CryptoCurrenciesMocks(scanResponse).ethereumAndStellar
|
||||
val currencies = MockCryptoCurrencyFactory(scanResponse).ethereumAndStellar
|
||||
val actual = finder.find(currencies)
|
||||
|
||||
Truth.assertThat(actual).containsExactly(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.tap.domain.userWalletList.implementation
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import com.tangem.common.test.domain.card.MockScanResponseFactory
|
||||
import com.tangem.domain.common.configs.GenericCardConfig
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.tap.domain.card.ScanResponseMockFactory
|
||||
import io.mockk.mockk
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
|
@ -194,11 +194,10 @@ internal class BiometricUserWalletsListManagerTest(private val model: Model) {
|
|||
return UserWallet(
|
||||
name = "Wallet $id",
|
||||
walletId = UserWalletId(stringValue = id),
|
||||
artworkUrl = "",
|
||||
cardsInWallet = emptySet(),
|
||||
isMultiCurrency = true,
|
||||
hasBackupError = false,
|
||||
scanResponse = ScanResponseMockFactory.create(
|
||||
scanResponse = MockScanResponseFactory.create(
|
||||
cardConfig = GenericCardConfig(maxWalletCount = 1),
|
||||
derivedKeys = emptyMap(),
|
||||
).let {
|
||||
|
|
|
|||
|
|
@ -59,41 +59,9 @@ val assembleInternalQA by tasks.registering {
|
|||
}
|
||||
}
|
||||
|
||||
val assembleExternalQA by tasks.registering {
|
||||
group = "build"
|
||||
description = "Builds external APK to 'build/outputs' directory"
|
||||
|
||||
val appOutputApkDir = "$projectDir/app/build/outputs/apk/external"
|
||||
val rootOutputApkDir = "$buildDir/outputs"
|
||||
val injected = objects.newInstance<Injected>()
|
||||
|
||||
dependsOn(":app:assembleExternal")
|
||||
|
||||
doFirst {
|
||||
injected.fs.delete {
|
||||
delete(appOutputApkDir)
|
||||
delete("$rootOutputApkDir/app-external.apk")
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
injected.fs.copy {
|
||||
from("$appOutputApkDir/app-external.apk")
|
||||
into(rootOutputApkDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val assembleQA by tasks.registering {
|
||||
group = "build"
|
||||
description = "Builds internal and external APKs to 'build/outputs' directory"
|
||||
|
||||
dependsOn(assembleInternalQA)
|
||||
dependsOn(assembleExternalQA)
|
||||
}
|
||||
|
||||
val generateComposeMetrics by tasks.registering {
|
||||
group = "other"
|
||||
description = "Build external APK and generates compose metrics to 'build/compose-metrics' directory"
|
||||
description = "Build internal APK and generates compose metrics to 'build/compose-metrics' directory"
|
||||
|
||||
subprojects {
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
|
|
@ -118,5 +86,5 @@ val generateComposeMetrics by tasks.registering {
|
|||
}
|
||||
}
|
||||
|
||||
finalizedBy(assembleExternalQA)
|
||||
finalizedBy(assembleInternalQA)
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ dependencies {
|
|||
implementation(projects.domain.markets.models)
|
||||
implementation(projects.domain.onramp.models)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.nft.models)
|
||||
|
||||
/* Libs - Other */
|
||||
api(deps.kotlin.serialization)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import com.tangem.core.decompose.navigation.Route
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.onramp.model.OnrampSource
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
|
@ -146,10 +146,21 @@ sealed class AppRoute(val path: String) : Route {
|
|||
data object WalletConnectSessions : AppRoute(path = "/wallet_connect_sessions")
|
||||
|
||||
@Serializable
|
||||
data class QrScanning(
|
||||
val source: SourceType,
|
||||
val networkName: String? = null,
|
||||
) : AppRoute(path = "/$source/qr_scanning${if (networkName != null) "/$networkName" else ""}")
|
||||
data class QrScanning(val source: Source) : AppRoute(path = "/$source/qr_scanning${source.path}") {
|
||||
|
||||
@Serializable
|
||||
sealed class Source {
|
||||
val path: String
|
||||
get() = when (this) {
|
||||
is Send -> "/$networkName"
|
||||
WalletConnect -> ""
|
||||
}
|
||||
|
||||
data class Send(val networkName: String) : Source()
|
||||
|
||||
data object WalletConnect : Source()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ReferralProgram(
|
||||
|
|
@ -255,7 +266,9 @@ sealed class AppRoute(val path: String) : Route {
|
|||
|
||||
enum class Mode {
|
||||
Onboarding, // general Mode
|
||||
AddBackup, // continue backup process for existing wallet 1
|
||||
AddBackupWallet1, // continue backup process for existing wallet 1
|
||||
WelcomeOnlyTwin, // show welcome screen and then navigate to wallet for twins
|
||||
RecreateWalletTwin, // reset twins
|
||||
ContinueFinalize, // continue finalize process (unfinished backup dialog)
|
||||
}
|
||||
}
|
||||
|
|
@ -266,4 +279,33 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val nextScreen: AppRoute,
|
||||
val screenSource: String,
|
||||
) : AppRoute(path = "/stories$storyId")
|
||||
|
||||
@Serializable
|
||||
data class NFTCollections(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/nft_collections/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class NFTReceive(
|
||||
val userWalletId: UserWalletId,
|
||||
) : AppRoute(path = "/nft_receive/${userWalletId.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class NFTDetails(
|
||||
val userWalletId: UserWalletId,
|
||||
val nftAsset: NFTAsset,
|
||||
val collectionName: String,
|
||||
) : AppRoute(path = "/nft_details/${userWalletId.stringValue}/${nftAsset.collectionId}/${nftAsset.id.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class NFTAssetTraits(
|
||||
val nftAsset: NFTAsset,
|
||||
) : AppRoute(path = "/nft_traits/${nftAsset.collectionId}/${nftAsset.id.stringValue}")
|
||||
|
||||
@Serializable
|
||||
data class NFTSend(
|
||||
val userWalletId: UserWalletId,
|
||||
val nftAsset: NFTAsset,
|
||||
val nftCollectionName: String,
|
||||
) : AppRoute(path = "/send/nft/${userWalletId.stringValue}/$nftCollectionName/${nftAsset.id}")
|
||||
}
|
||||
1
common/test/.gitignore
vendored
Normal file
1
common/test/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
30
common/test/build.gradle.kts
Normal file
30
common/test/build.gradle.kts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.common.test"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(projects.data.common)
|
||||
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
implementation(deps.androidx.datastore)
|
||||
implementation(deps.test.coroutine)
|
||||
|
||||
implementation(tangemDeps.blockchain)
|
||||
implementation(tangemDeps.card.core)
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.common.test.data.quote
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object MockQuoteResponseFactory {
|
||||
|
||||
fun createSinglePrice(value: BigDecimal): QuotesResponse.Quote {
|
||||
return QuotesResponse.Quote(
|
||||
price = value,
|
||||
priceChange24h = value,
|
||||
priceChange1w = value,
|
||||
priceChange30d = value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.common.test.data.quote
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
import com.tangem.datasource.local.quote.converter.QuoteConverter
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
|
||||
fun QuotesResponse.Quote.toDomain(rawCurrencyId: String, source: StatusSource = StatusSource.ACTUAL): Quote {
|
||||
return QuoteConverter(source = source).convert(value = mapOf(rawCurrencyId to this).entries.first())
|
||||
}
|
||||
|
||||
fun Pair<String, QuotesResponse.Quote>.toDomain(source: StatusSource = StatusSource.ACTUAL): Quote {
|
||||
return QuoteConverter(source = source).convert(value = mapOf(this).entries.first())
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.common.test.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
|
||||
class MockStateDataStore<T>(default: T) : DataStore<T> {
|
||||
|
||||
private val _data = MutableStateFlow(value = default)
|
||||
override val data: Flow<T> = _data
|
||||
|
||||
override suspend fun updateData(transform: suspend (t: T) -> T): T {
|
||||
return _data.updateAndGet { transform(it) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.tap.domain.card
|
||||
package com.tangem.common.test.domain.card
|
||||
|
||||
import com.tangem.common.card.CardWallet
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
|
|
@ -14,7 +14,7 @@ import java.util.Date
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object ScanResponseMockFactory {
|
||||
object MockScanResponseFactory {
|
||||
|
||||
private val genericFirmwareVersion = CardDTO.FirmwareVersion(
|
||||
major = 3,
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue