Updated on 2026-08-14

This commit is contained in:
Tangem 2023-07-24 13:45:56 +03:00
commit ea661f55ab
250 changed files with 6169 additions and 949 deletions

View file

@ -25,11 +25,13 @@ dependencies {
implementation(project(":domain:legacy")) implementation(project(":domain:legacy"))
implementation(project(":domain:models")) implementation(project(":domain:models"))
implementation(project(":domain:core")) implementation(project(":domain:core"))
implementation(project(":domain:card")) implementation(projects.domain.card)
implementation(project(":domain:wallets")) implementation(project(":domain:wallets"))
implementation(project(":domain:wallets:models")) implementation(project(":domain:wallets:models"))
implementation(projects.domain.tokens)
implementation(project(":common")) implementation(project(":common"))
implementation(project(":core:analytics")) implementation(project(":core:analytics"))
implementation(projects.core.analytics.models)
implementation(project(":core:navigation")) implementation(project(":core:navigation"))
implementation(project(":core:featuretoggles")) implementation(project(":core:featuretoggles"))
implementation(project(":core:res")) implementation(project(":core:res"))
@ -39,6 +41,9 @@ dependencies {
implementation(project(":libs:crypto")) implementation(project(":libs:crypto"))
implementation(project(":libs:auth")) implementation(project(":libs:auth"))
implementation(project(":data:source:preferences")) implementation(project(":data:source:preferences"))
implementation(projects.data.card)
implementation(projects.data.tokens)
implementation(projects.data.common)
/** Features */ /** Features */
implementation(project(":features:onboarding")) implementation(project(":features:onboarding"))
@ -119,13 +124,6 @@ dependencies {
implementation(deps.appsflyer) implementation(deps.appsflyer)
implementation(deps.amplitude) implementation(deps.amplitude)
implementation(deps.kotsonGson) implementation(deps.kotsonGson)
//TODO: refactoring: remove it when all network services moved to the datasource module
implementation(deps.retrofit)
implementation(deps.retrofit.moshi)
implementation(deps.moshi)
implementation(deps.moshi.kotlin)
implementation(deps.okHttp)
implementation(deps.okHttp.logging)
implementation(deps.zendesk.chat) implementation(deps.zendesk.chat)
implementation(deps.zendesk.messaging) implementation(deps.zendesk.messaging)
implementation(deps.spongecastle.core) implementation(deps.spongecastle.core)
@ -144,6 +142,7 @@ dependencies {
implementation(deps.kotlin.serialization) implementation(deps.kotlin.serialization)
implementation(deps.walletConnectCore) implementation(deps.walletConnectCore)
implementation(deps.walletConnectWeb3) implementation(deps.walletConnectWeb3)
implementation(deps.prettyLogger)
/** Testing libraries */ /** Testing libraries */
testImplementation(deps.test.junit) testImplementation(deps.test.junit)

View file

@ -10,10 +10,11 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import by.kirich1409.viewbindingdelegate.viewBinding import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.data.card.sdk.CardSdkLifecycleObserver
import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
@ -56,7 +57,6 @@ import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
lateinit var tangemSdk: TangemSdk
lateinit var tangemSdkManager: TangemSdkManager lateinit var tangemSdkManager: TangemSdkManager
lateinit var backupService: BackupService lateinit var backupService: BackupService
internal var lockUserWalletsTimer: LockUserWalletsTimer? = null internal var lockUserWalletsTimer: LockUserWalletsTimer? = null
@ -88,7 +88,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lateinit var testerRouter: TesterRouter lateinit var testerRouter: TesterRouter
@Inject @Inject
lateinit var injectedTangemSdk: TangemSdk lateinit var cardSdkLifecycleObserver: CardSdkLifecycleObserver
@Inject
lateinit var cardSdkConfigRepository: CardSdkConfigRepository
@Inject @Inject
lateinit var injectedTangemSdkManager: TangemSdkManager lateinit var injectedTangemSdkManager: TangemSdkManager
@ -122,11 +125,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
systemActions() systemActions()
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
tangemSdk = injectedTangemSdk cardSdkLifecycleObserver.onCreate(context = this)
tangemSdkManager = injectedTangemSdkManager tangemSdkManager = injectedTangemSdkManager
appStateHolder.tangemSdkManager = tangemSdkManager appStateHolder.tangemSdkManager = tangemSdkManager
appStateHolder.tangemSdk = tangemSdk backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
backupService = BackupService.init(tangemSdk, this)
lockUserWalletsTimer = LockUserWalletsTimer(owner = this) lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
initUserWalletsListManager() initUserWalletsListManager()
@ -144,10 +147,37 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
walletRouter = walletRouter, walletRouter = walletRouter,
walletConnectInteractor = walletConnectInteractor, walletConnectInteractor = walletConnectInteractor,
tokenDetailsRouter = tokenDetailsRouter, tokenDetailsRouter = tokenDetailsRouter,
cardSdkConfigRepository = cardSdkConfigRepository,
), ),
) )
} }
override fun onStart() {
super.onStart()
dialogManager.onStart(this)
}
override fun onResume() {
super.onResume()
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
navigateToInitialScreenIfNeededOnResume(intent)
}
override fun onStop() {
notificationsHandler = null
dialogManager.onStop()
super.onStop()
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
intentProcessor.removeAll()
cardSdkLifecycleObserver.onDestroy()
super.onDestroy()
}
private fun initIntentHandlers() { private fun initIntentHandlers() {
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true } val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider)) intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider))
@ -183,13 +213,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} }
override fun onResume() {
super.onResume()
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
navigateToInitialScreenIfNeededOnResume(intent)
}
override fun onNewIntent(intent: Intent?) { override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent) super.onNewIntent(intent)
scope.launch { scope.launch {
@ -197,23 +220,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
} }
} }
override fun onStart() {
super.onStart()
dialogManager.onStart(this)
}
override fun onStop() {
notificationsHandler = null
dialogManager.onStop()
super.onStop()
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
intentProcessor.removeAll()
super.onDestroy()
}
override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) { override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) {
if (snackbar != null) return if (snackbar != null) return

View file

@ -5,6 +5,8 @@ import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import coil.ImageLoader import coil.ImageLoader
import coil.ImageLoaderFactory import coil.ImageLoaderFactory
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log import com.tangem.Log
import com.tangem.LogFormat import com.tangem.LogFormat
import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.BlockchainSdkConfig
@ -14,12 +16,14 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
import com.tangem.data.source.preferences.PreferencesDataSource import com.tangem.data.source.preferences.PreferencesDataSource
import com.tangem.datasource.api.common.MoshiConverter import com.tangem.datasource.api.common.MoshiConverter
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.asset.AssetReader
import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.config.ConfigManager
import com.tangem.datasource.config.FeaturesLocalLoader import com.tangem.datasource.config.FeaturesLocalLoader
import com.tangem.datasource.config.models.Config import com.tangem.datasource.config.models.Config
import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.DomainLayer import com.tangem.domain.DomainLayer
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.common.LogConfig import com.tangem.domain.common.LogConfig
import com.tangem.domain.wallets.legacy.WalletManagersRepository import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
@ -59,7 +63,6 @@ import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.wallet.BuildConfig import com.tangem.wallet.BuildConfig
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import okhttp3.logging.HttpLoggingInterceptor
import org.rekotlin.Store import org.rekotlin.Store
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -151,6 +154,9 @@ class TapApplication : Application(), ImageLoaderFactory {
@Inject @Inject
lateinit var tokenDetailsFeatureToggles: TokenDetailsFeatureToggles lateinit var tokenDetailsFeatureToggles: TokenDetailsFeatureToggles
@Inject
lateinit var scanCardProcessor: ScanCardProcessor
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -168,12 +174,20 @@ class TapApplication : Application(), ImageLoaderFactory {
walletConnectRepository = walletConnect2Repository, walletConnectRepository = walletConnect2Repository,
walletConnectSessionsRepository = walletConnectSessionsRepository, walletConnectSessionsRepository = walletConnectSessionsRepository,
tokenDetailsFeatureToggles = tokenDetailsFeatureToggles, tokenDetailsFeatureToggles = tokenDetailsFeatureToggles,
scanCardProcessor = scanCardProcessor,
), ),
), ),
) )
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree()) Logger.addLogAdapter(AndroidLogAdapter())
Timber.plant(
object : Timber.DebugTree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
Logger.log(priority, tag, message, t)
}
},
)
} }
foregroundActivityObserver = ForegroundActivityObserver() foregroundActivityObserver = ForegroundActivityObserver()
@ -192,7 +206,7 @@ class TapApplication : Application(), ImageLoaderFactory {
if (LogConfig.network.blockchainSdkNetwork) { if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf( BlockchainSdkRetrofitBuilder.interceptors = listOf(
HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }, createNetworkLoggingInterceptor(),
) )
} }

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -13,7 +13,6 @@ sealed class MainScreen(
class ScreenOpened : MainScreen("Screen opened") class ScreenOpened : MainScreen("Screen opened")
class ButtonScanCard : MainScreen("Button - Scan Card") class ButtonScanCard : MainScreen("Button - Scan Card")
class ButtonMyWallets : MainScreen("Button - My Wallets") class ButtonMyWallets : MainScreen("Button - My Wallets")
class ButtonBuy : MainScreen("Button - Buy")
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen( class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
event = "Enable Biometric", event = "Enable Biometric",

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.extensions.filterNotNull import com.tangem.tap.common.extensions.filterNotNull

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
sealed class MyWallets( sealed class MyWallets(
event: String, event: String,

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
@ -72,26 +72,8 @@ sealed class Onboarding(
class SetupFinished : Twins("Twin Setup Finished") class SetupFinished : Twins("Twin Setup Finished")
} }
class PinScreenOpened : Onboarding("Onboarding", "PIN screen opened")
class ButtonSetPinCode : Onboarding("Onboarding", "Button - Set PIN Code")
class CardConnectionScreenOpened : Onboarding("Onboarding", "Card Connection Screen Opened")
class ButtonConnect : Onboarding("Onboarding", "Button - Connect")
class PinCodeSet : Onboarding("Onboarding", "PIN Code Set")
class KYCScreenOpened : Onboarding("Onboarding", "KYC screen opened")
class KYCStarted : Onboarding("Onboarding", "KYC Started")
class KYCInProgress : Onboarding("Onboarding", "KYC In Progress")
class KYCRejected : Onboarding("Onboarding", "KYC Rejected")
class ClaimScreenOpened : Onboarding("Onboarding", "Claim Screen Opened")
class ButtonClaim : Onboarding("Onboarding", "Button - Claim")
class ClaimWasSuccessfully : Onboarding("Onboarding", "Claim Was Successfully")
class ButtonChat : Onboarding("Onboarding", "Button - Chat") class ButtonChat : Onboarding("Onboarding", "Button - Chat")
class NotEnoughGasError : Onboarding("Onboarding", "Not Enough Gas Error")
class CardNotPassedError : Onboarding("Onboarding", "Card Not Passed Error")
class EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding( class EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding(
category = "Onboarding / Biometric", category = "Onboarding / Biometric",
event = "Enable Biometric", event = "Enable Biometric",

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.features.details.ui.details.SocialNetwork import com.tangem.tap.features.details.ui.details.SocialNetwork
/** /**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull import com.tangem.tap.common.extensions.filterNotNull
/** /**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType
/** /**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.events package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer package com.tangem.tap.common.analytics.handlers.appsFlyer
import com.appsflyer.AFInAppEventType import com.appsflyer.AFInAppEventType
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.analytics.events.Shop

View file

@ -1,9 +1,9 @@
package com.tangem.tap.common.analytics.handlers.firebase package com.tangem.tap.common.analytics.handlers.firebase
import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.FirebaseAnalytics
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.ErrorEventHandler import com.tangem.core.analytics.api.ErrorEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.analytics.events.Shop

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.paramsInterceptor package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse

View file

@ -1,14 +1,14 @@
package com.tangem.tap.common.analytics.paramsInterceptor package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class LinkedCardContextInterceptor( class LinkedCardContextInterceptor(
private val scanResponse: ScanResponse, scanResponse: ScanResponse,
val parent: LinkedCardContextInterceptor? = null, val parent: LinkedCardContextInterceptor? = null,
) : ParamsInterceptor { ) : ParamsInterceptor {

View file

@ -9,6 +9,7 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.common.analytics.converters.TopUpEventConverter import com.tangem.tap.common.analytics.converters.TopUpEventConverter
@ -17,7 +18,6 @@ import com.tangem.tap.common.extensions.copy
import com.tangem.tap.domain.model.TotalFiatBalance import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
import com.tangem.tap.domain.walletStores.WalletStoresManager import com.tangem.tap.domain.walletStores.WalletStoresManager
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency

View file

@ -12,6 +12,7 @@ import com.tangem.tap.common.chat.opener.implementation.SprinklrChatOpener
import com.tangem.tap.common.chat.opener.implementation.ZendeskChatOpener import com.tangem.tap.common.chat.opener.implementation.ZendeskChatOpener
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import org.rekotlin.Store import org.rekotlin.Store
import java.io.File
class ChatManager( class ChatManager(
private val preferencesStorage: PreferencesDataSource, private val preferencesStorage: PreferencesDataSource,
@ -20,7 +21,7 @@ class ChatManager(
) { ) {
private val openers = mutableMapOf<ChatConfig, ChatOpener>() private val openers = mutableMapOf<ChatConfig, ChatOpener>()
fun open(config: ChatConfig, feedbackDataBuilder: (Context) -> String) { fun open(config: ChatConfig, createLogsFile: (Context) -> File?, createFeedbackFile: (Context) -> File?) {
val opener = openers.getOrPut(config) { val opener = openers.getOrPut(config) {
when (config) { when (config) {
is SprinklrConfig -> SprinklrChatOpener(getSprinklrUserId(), config, store, foregroundActivityObserver) is SprinklrConfig -> SprinklrChatOpener(getSprinklrUserId(), config, store, foregroundActivityObserver)
@ -28,7 +29,10 @@ class ChatManager(
} }
} }
opener.open(feedbackDataBuilder) opener.open(
createFeedbackFile = createFeedbackFile,
createLogsFile = createLogsFile,
)
} }
private fun getZendeskUserId(): String { private fun getZendeskUserId(): String {

View file

@ -1,7 +1,8 @@
package com.tangem.tap.common.chat.opener package com.tangem.tap.common.chat.opener
import android.content.Context import android.content.Context
import java.io.File
internal interface ChatOpener { internal interface ChatOpener {
fun open(feedbackDataBuilder: (Context) -> String) fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?)
} }

View file

@ -2,14 +2,15 @@ package com.tangem.tap.common.chat.opener.implementation
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.tangem.datasource.config.models.SprinklrConfig
import com.tangem.tap.ForegroundActivityObserver import com.tangem.tap.ForegroundActivityObserver
import com.tangem.tap.common.chat.opener.ChatOpener import com.tangem.tap.common.chat.opener.ChatOpener
import com.tangem.datasource.config.models.SprinklrConfig
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.sprinklr.redux.SprinklrAction import com.tangem.tap.features.sprinklr.redux.SprinklrAction
import com.tangem.tap.features.sprinklr.ui.SprinklrActivity import com.tangem.tap.features.sprinklr.ui.SprinklrActivity
import com.tangem.tap.withForegroundActivity import com.tangem.tap.withForegroundActivity
import org.rekotlin.Store import org.rekotlin.Store
import java.io.File
internal class SprinklrChatOpener( internal class SprinklrChatOpener(
private val userId: String, private val userId: String,
@ -17,7 +18,7 @@ internal class SprinklrChatOpener(
private val store: Store<AppState>, private val store: Store<AppState>,
private val foregroundActivityObserver: ForegroundActivityObserver, private val foregroundActivityObserver: ForegroundActivityObserver,
) : ChatOpener { ) : ChatOpener {
override fun open(feedbackDataBuilder: (Context) -> String) { override fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?) {
store.dispatch(SprinklrAction.Init(userId, config)) store.dispatch(SprinklrAction.Init(userId, config))
foregroundActivityObserver.withForegroundActivity { activity -> foregroundActivityObserver.withForegroundActivity { activity ->
val intent = Intent(activity, SprinklrActivity::class.java) val intent = Intent(activity, SprinklrActivity::class.java)

View file

@ -2,34 +2,34 @@ package com.tangem.tap.common.chat.opener.implementation
import android.content.Context import android.content.Context
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.datasource.config.models.ZendeskConfig
import com.tangem.domain.common.LogConfig import com.tangem.domain.common.LogConfig
import com.tangem.tap.ForegroundActivityObserver import com.tangem.tap.ForegroundActivityObserver
import com.tangem.tap.common.chat.opener.ChatOpener import com.tangem.tap.common.chat.opener.ChatOpener
import com.tangem.datasource.config.models.ZendeskConfig
import com.tangem.tap.withForegroundActivity import com.tangem.tap.withForegroundActivity
import com.tangem.wallet.R import com.tangem.wallet.R
import com.zendesk.logger.Logger import com.zendesk.logger.Logger
import zendesk.chat.Chat import timber.log.Timber
import zendesk.chat.ChatConfiguration import zendesk.chat.*
import zendesk.chat.ChatEngine
import zendesk.chat.ChatProvidersConfiguration
import zendesk.chat.VisitorInfo
import zendesk.configurations.Configuration import zendesk.configurations.Configuration
import zendesk.messaging.MessagingActivity import zendesk.messaging.MessagingActivity
import java.io.File
internal class ZendeskChatOpener( internal class ZendeskChatOpener(
private val userId: String, private val userId: String,
private val config: ZendeskConfig, private val config: ZendeskConfig,
private val foregroundActivityObserver: ForegroundActivityObserver, private val foregroundActivityObserver: ForegroundActivityObserver,
) : ChatOpener { ) : ChatOpener {
private var isInitialized = false private var isInitialized = false
override fun open(feedbackDataBuilder: (Context) -> String) { override fun open(createFeedbackFile: (Context) -> File?, createLogsFile: (Context) -> File?) {
foregroundActivityObserver.withForegroundActivity { activity -> foregroundActivityObserver.withForegroundActivity { activity ->
initZendeskIfNeeded(activity.applicationContext) initZendeskIfNeeded(activity.applicationContext)
setChatVisitorInfo() setChatVisitorInfo()
setChatVisitorNote(feedbackDataBuilder(activity))
showMessagingActivity(activity) showMessagingActivity(activity)
sendFeedbackFile(createFeedbackFile(activity))
sendLogsFile(createLogsFile(activity))
} }
} }
@ -48,8 +48,20 @@ internal class ZendeskChatOpener(
ChatProvidersConfiguration.builder().withVisitorInfo(visitorInfo).build() ChatProvidersConfiguration.builder().withVisitorInfo(visitorInfo).build()
} }
private fun setChatVisitorNote(note: String) { private fun sendFeedbackFile(feedbackFile: File?) {
Chat.INSTANCE.providers()?.profileProvider()?.setVisitorNote(note) if (isInitialized && feedbackFile != null) {
Chat.INSTANCE.providers()?.chatProvider()?.sendFile(feedbackFile) { _, bytesUploaded, _ ->
Timber.d("Log file sent", "bytesUploaded: $bytesUploaded")
}
}
}
private fun sendLogsFile(logsFile: File?) {
if (isInitialized && logsFile != null) {
Chat.INSTANCE.providers()?.chatProvider()?.sendFile(logsFile) { _, bytesUploaded, _ ->
Timber.d("Log file sent", "bytesUploaded: $bytesUploaded")
}
}
} }
private fun showMessagingActivity(context: Context) { private fun showMessagingActivity(context: Context) {

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.di.domain.wallets
import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.legacy.WalletsStateHolder
import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -17,4 +18,10 @@ object WalletsDomainModule {
fun providesGetWalletsUseCase(walletsStateHolder: WalletsStateHolder): GetWalletsUseCase { fun providesGetWalletsUseCase(walletsStateHolder: WalletsStateHolder): GetWalletsUseCase {
return GetWalletsUseCase(walletsStateHolder = walletsStateHolder) return GetWalletsUseCase(walletsStateHolder = walletsStateHolder)
} }
@Provides
@ViewModelScoped
fun providesSaveWalletUseCase(walletsStateHolder: WalletsStateHolder): SaveWalletUseCase {
return SaveWalletUseCase(walletsStateHolder = walletsStateHolder)
}
} }

View file

@ -24,13 +24,17 @@ fun BigDecimal.toFormattedString(
df.decimalFormatSymbols = symbols df.decimalFormatSymbols = symbols
df.maximumFractionDigits = decimals df.maximumFractionDigits = decimals
df.minimumFractionDigits = 0 df.minimumFractionDigits = 0
df.isGroupingUsed = false df.isGroupingUsed = true
df.roundingMode = roundingMode df.roundingMode = roundingMode
return df.format(this) return df.format(this)
} }
/**
* To formatted crypto currency string
* Specific method because there is no crypto currency codes in Locale
*/
@Suppress("MagicNumber") @Suppress("MagicNumber")
fun BigDecimal.toFormattedCurrencyString( fun BigDecimal.toFormattedCryptoCurrencyString(
decimals: Int, decimals: Int,
currency: String, currency: String,
roundingMode: RoundingMode = RoundingMode.DOWN, roundingMode: RoundingMode = RoundingMode.DOWN,
@ -41,16 +45,34 @@ fun BigDecimal.toFormattedCurrencyString(
} else { } else {
decimals decimals
} }
try {
val locale = Locale.getDefault()
val formatter = NumberFormat.getCurrencyInstance(locale)
// first create currency instance for "USD" with Locale default to replace currency to crypto later
Currency.getInstance("USD")?.let { currencyTmp ->
formatter.currency = currencyTmp
formatter.maximumFractionDigits = decimalsForRounding
formatter.minimumFractionDigits = 0
formatter.isGroupingUsed = true
formatter.roundingMode = roundingMode
// cause formatter created for USD, replace Currency with Crypto symbol on right by Locale place
return formatter.format(this).replace(currencyTmp.getSymbol(locale), "$currency")
}
} catch (e: IllegalArgumentException) {
Timber.e(e, "can't parse currency")
}
// if something went wrong - use old way to format
val formattedAmount = this.toFormattedString( val formattedAmount = this.toFormattedString(
decimals = decimalsForRounding, decimals = decimalsForRounding,
roundingMode = roundingMode, roundingMode = roundingMode,
locale = Locale.getDefault(),
) )
return "$formattedAmount $currency" return "$formattedAmount $currency"
} }
fun BigDecimal.toFiatRateString(fiatCurrencyName: String, fiatCode: String): String { fun BigDecimal.toFiatRateString(fiatCurrencyName: String, fiatCode: String): String {
try { try {
val formatter = NumberFormat.getCurrencyInstance() val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
Currency.getInstance(fiatCode)?.let { currency -> Currency.getInstance(fiatCode)?.let { currency ->
formatter.currency = currency formatter.currency = currency
formatter.maximumFractionDigits = 2 formatter.maximumFractionDigits = 2
@ -91,7 +113,7 @@ fun BigDecimal.toFormattedFiatValue(
formatWithSpaces: Boolean = false, formatWithSpaces: Boolean = false,
): String { ): String {
try { try {
val formatter = NumberFormat.getCurrencyInstance() val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault())
Currency.getInstance(fiatCode)?.let { currency -> Currency.getInstance(fiatCode)?.let { currency ->
formatter.currency = currency formatter.currency = currency
formatter.maximumFractionDigits = 2 formatter.maximumFractionDigits = 2

View file

@ -1,17 +1,12 @@
package com.tangem.tap.common.feedback package com.tangem.tap.common.feedback
import android.os.Build import android.os.Build
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.Address
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
class AdditionalFeedbackInfo { class AdditionalFeedbackInfo {
class EmailWalletInfo( class EmailWalletInfo(

View file

@ -1,9 +1,9 @@
package com.tangem.tap.common.feedback package com.tangem.tap.common.feedback
import android.content.Context import android.content.Context
import com.tangem.datasource.config.models.ChatConfig
import com.tangem.domain.common.TapWorkarounds import com.tangem.domain.common.TapWorkarounds
import com.tangem.tap.common.chat.ChatManager import com.tangem.tap.common.chat.ChatManager
import com.tangem.datasource.config.models.ChatConfig
import com.tangem.tap.common.extensions.sendEmail import com.tangem.tap.common.extensions.sendEmail
import com.tangem.tap.common.log.TangemLogCollector import com.tangem.tap.common.log.TangemLogCollector
import com.tangem.tap.foregroundActivityObserver import com.tangem.tap.foregroundActivityObserver
@ -22,32 +22,61 @@ class FeedbackManager(
private val chatManager: ChatManager, private val chatManager: ChatManager,
) { ) {
private var sessionFeedbackFile: File? = null
private var sessionLogsFile: File? = null
fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) { fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) {
feedbackData.prepare(infoHolder) feedbackData.prepare(infoHolder)
foregroundActivityObserver.withForegroundActivity { activity -> foregroundActivityObserver.withForegroundActivity { activity ->
val fileLog = createLogFile(activity)
activity.sendEmail( activity.sendEmail(
email = getSupportEmail(), email = getSupportEmail(),
subject = activity.getString(feedbackData.subjectResId), subject = activity.getString(feedbackData.subjectResId),
message = feedbackData.joinTogether(activity, infoHolder), message = feedbackData.joinTogether(activity, infoHolder),
file = fileLog, file = getLogFile(activity),
onFail = onFail, onFail = onFail,
) )
} }
} }
fun openChat(config: ChatConfig, feedbackData: FeedbackData) { fun openChat(config: ChatConfig, feedbackData: FeedbackData) {
chatManager.open(config) { context -> chatManager.open(
feedbackData.run { config = config,
createLogsFile = ::getLogFile,
createFeedbackFile = { context -> getFeedbackFile(context, feedbackData) },
)
}
private fun getFeedbackFile(context: Context, feedbackData: FeedbackData): File? {
return try {
if (sessionFeedbackFile != null) {
return sessionFeedbackFile
}
val file = File(context.filesDir, FEEDBACK_FILE)
file.delete()
file.createNewFile()
val feedback = feedbackData.run {
prepare(infoHolder) prepare(infoHolder)
joinTogether(context, infoHolder) joinTogether(context, infoHolder)
} }
val fileWriter = FileWriter(file)
fileWriter.write(feedback)
fileWriter.close()
sessionFeedbackFile = file
sessionFeedbackFile
} catch (ex: Exception) {
Timber.e(ex, "Can't create the logs file")
null
} }
} }
private fun createLogFile(context: Context): File? { private fun getLogFile(context: Context): File? {
return try { return try {
val file = File(context.filesDir, "logs.txt") if (sessionLogsFile != null) {
return sessionLogsFile
}
val file = File(context.filesDir, LOGS_FILE)
file.delete() file.delete()
file.createNewFile() file.createNewFile()
@ -57,7 +86,8 @@ class FeedbackManager(
fileWriter.write(stringWriter.toString()) fileWriter.write(stringWriter.toString())
fileWriter.close() fileWriter.close()
logCollector.clearLogs() logCollector.clearLogs()
file sessionLogsFile = file
sessionLogsFile
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.e(ex, "Can't create the logs file") Timber.e(ex, "Can't create the logs file")
null null
@ -75,5 +105,7 @@ class FeedbackManager(
companion object { companion object {
const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com" const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com"
const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com" const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com"
const val FEEDBACK_FILE = "feedback.txt"
const val LOGS_FILE = "logs.txt"
} }
} }

View file

@ -4,8 +4,8 @@ import android.content.Context
import android.util.Log import android.util.Log
import coil.ImageLoader import coil.ImageLoader
import coil.util.Logger import coil.util.Logger
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber import timber.log.Timber
private const val COIL_LOG_TAG = "COIL" private const val COIL_LOG_TAG = "COIL"
@ -18,14 +18,7 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
logger(CoilTimberLogger()) logger(CoilTimberLogger())
okHttpClient { okHttpClient {
OkHttpClient.Builder() OkHttpClient.Builder()
.addNetworkInterceptor( .addNetworkInterceptor(createNetworkLoggingInterceptor())
HttpLoggingInterceptor { message ->
Timber.tag(COIL_LOG_TAG).d(message)
}
.apply {
level = HttpLoggingInterceptor.Level.BODY
},
)
.build() .build()
} }
} }

View file

@ -8,6 +8,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result import com.google.zxing.Result
import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE
import me.dm7.barcodescanner.zxing.ZXingScannerView import me.dm7.barcodescanner.zxing.ZXingScannerView
@ -16,11 +17,15 @@ import me.dm7.barcodescanner.zxing.ZXingScannerView
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler { class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
private lateinit var mScannerView: ZXingScannerView private lateinit var mScannerView: ZXingScannerView
override fun onCreate(state: Bundle?) { override fun onCreate(state: Bundle?) {
super.onCreate(state) super.onCreate(state)
mScannerView = ZXingScannerView(this)
mScannerView = ZXingScannerView(this).apply {
setFormats(listOf(BarcodeFormat.QR_CODE))
}
setContentView(mScannerView) setContentView(mScannerView)
if (!permissionIsGranted()) requestPermission() if (!permissionIsGranted()) requestPermission()

View file

@ -3,7 +3,8 @@ package com.tangem.tap.common.redux
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.preferencesStorage import com.tangem.tap.preferencesStorage
import com.tangem.tap.tangemSdkManager import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import org.rekotlin.Middleware import org.rekotlin.Middleware
class AccessCodeRequestPolicyMiddleware { class AccessCodeRequestPolicyMiddleware {
@ -19,9 +20,8 @@ class AccessCodeRequestPolicyMiddleware {
} }
private fun updateAccessCodeRequestPolicy(scanResponse: ScanResponse) { private fun updateAccessCodeRequestPolicy(scanResponse: ScanResponse) {
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes && isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes && scanResponse.card.isAccessCodeSet,
scanResponse.card.isAccessCodeSet,
) )
} }
} }

View file

@ -1,42 +1,43 @@
package com.tangem.tap.di package com.tangem.tap.di
import android.content.Context import android.content.Context
import androidx.fragment.app.FragmentActivity
import com.tangem.TangemSdk
import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.ScanCardUseCase
import com.tangem.sdk.extensions.initWithBiometrics import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
import com.tangem.tap.userTokensRepository import com.tangem.tap.userTokensRepository
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.components.SingletonComponent
import dagger.hilt.android.scopes.ActivityScoped import javax.inject.Singleton
@Module @Module
@InstallIn(ActivityComponent::class) @InstallIn(SingletonComponent::class)
internal object ActivityModule { internal object ActivityModule {
@Provides @Provides
@ActivityScoped @Singleton
fun provideTangemSdk(@ActivityContext context: Context): TangemSdk { fun provideTangemSdkManager(
return TangemSdk.initWithBiometrics(context as FragmentActivity, TangemSdkManager.config) @ApplicationContext context: Context,
cardSdkConfigRepository: CardSdkConfigRepository,
): TangemSdkManager {
return TangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
} }
@Provides @Provides
@ActivityScoped @Singleton
fun provideTangemSdkManager(@ActivityContext context: Context, tangemSdk: TangemSdk): TangemSdkManager { fun provideScanCardUseCase(
return TangemSdkManager(tangemSdk, context) tangemSdkManager: TangemSdkManager,
} cardSdkConfigRepository: CardSdkConfigRepository,
): ScanCardUseCase {
@Provides
@ActivityScoped
fun provideScanCardUseCase(tangemSdk: TangemSdk, tangemSdkManager: TangemSdkManager): ScanCardUseCase {
return ScanCardUseCase( return ScanCardUseCase(
tangemSdk = tangemSdk, cardSdkConfigRepository = cardSdkConfigRepository,
scanCardRepository = DefaultScanCardRepository(userTokensRepository, tangemSdkManager), scanCardRepository = DefaultScanCardRepository(
userTokensRepository = userTokensRepository,
tangemSdkManager = tangemSdkManager,
),
) )
} }
} }

View file

@ -0,0 +1,46 @@
package com.tangem.tap.di.domain
import com.tangem.domain.card.GetAccessCodeSavingStatusUseCase
import com.tangem.domain.card.GetBiometricsStatusUseCase
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.SetAccessCodeRequestPolicyUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor
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 CardDomainModule {
@Provides
@Singleton
fun provideScanCardUseCase(): ScanCardProcessor = DefaultScanCardProcessor()
@Provides
@Singleton
fun provideGetBiometricsStatusUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): GetBiometricsStatusUseCase {
return GetBiometricsStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
@Provides
@Singleton
fun provideSetAccessCodeRequestPolicyUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): SetAccessCodeRequestPolicyUseCase {
return SetAccessCodeRequestPolicyUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
@Provides
@Singleton
fun provideGetAccessCodeSavingStatusUseCase(
cardSdkConfigRepository: CardSdkConfigRepository,
): GetAccessCodeSavingStatusUseCase {
return GetAccessCodeSavingStatusUseCase(cardSdkConfigRepository = cardSdkConfigRepository)
}
}

View file

@ -0,0 +1,34 @@
package com.tangem.tap.di.domain
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.tokens.repository.TokensRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@Module
@InstallIn(ViewModelComponent::class)
internal object TokensDomainModule {
@Provides
@ViewModelScoped
fun provideGetTokenListUseCase(
tokensRepository: TokensRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
dispatchers: CoroutineDispatcherProvider,
): GetTokenListUseCase {
return GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideToggleTokenListSortingUseCase(dispatchers: CoroutineDispatcherProvider): ToggleTokenListSortingUseCase {
return ToggleTokenListSortingUseCase(dispatchers)
}
}

View file

@ -1,27 +1,25 @@
package com.tangem.tap.domain package com.tangem.tap.domain
import android.content.Context import android.content.res.Resources
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.tangem.Message import com.tangem.Message
import com.tangem.TangemSdk import com.tangem.TangemSdk
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.common.* import com.tangem.common.*
import com.tangem.common.biometric.BiometricManager import com.tangem.common.biometric.BiometricManager
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.core.* import com.tangem.common.core.*
import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.usersCode.UserCodeRepository import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.crypto.bip39.DefaultMnemonic import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.operations.ScanTask import com.tangem.operations.ScanTask
import com.tangem.operations.derivation.DerivationTaskResponse import com.tangem.operations.derivation.DerivationTaskResponse
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.pins.CheckUserCodesCommand
import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.operations.pins.SetUserCodeCommand
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
@ -38,7 +36,10 @@ import kotlinx.coroutines.withContext
import kotlin.coroutines.resume import kotlin.coroutines.resume
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigRepository, private val resources: Resources) {
private val tangemSdk: TangemSdk
get() = cardSdkConfigRepository.sdk
private val userCodeRepository by lazy { private val userCodeRepository by lazy {
UserCodeRepository( UserCodeRepository(
@ -63,7 +64,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
messageRes: Int? = null, messageRes: Int? = null,
allowsRequestAccessCodeFromRepository: Boolean = false, allowsRequestAccessCodeFromRepository: Boolean = false,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
runnable = ScanProductTask( runnable = ScanProductTask(
card = null, card = null,
@ -80,7 +81,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsync( return runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver), CreateProductWalletTask(scanResponse.cardTypesResolver),
scanResponse.card.cardId, scanResponse.card.cardId,
Message(context.getString(R.string.initial_message_create_wallet_body)), Message(resources.getString(R.string.initial_message_create_wallet_body)),
) )
} }
@ -92,7 +93,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
is CompletionResult.Success -> runTaskAsync( is CompletionResult.Success -> runTaskAsync(
CreateProductWalletTask(scanResponse.cardTypesResolver, seedResult.data), CreateProductWalletTask(scanResponse.cardTypesResolver, seedResult.data),
scanResponse.card.cardId, scanResponse.card.cardId,
Message(context.getString(R.string.initial_message_create_wallet_body)), Message(resources.getString(R.string.initial_message_create_wallet_body)),
) )
is CompletionResult.Failure -> CompletionResult.Failure(seedResult.error) is CompletionResult.Failure -> CompletionResult.Failure(seedResult.error)
@ -111,7 +112,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
CreateWalletAndRescanTask(), CreateWalletAndRescanTask(),
cardId, cardId,
initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)), initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)),
) )
.map { CardDTO(it) } .map { CardDTO(it) }
} }
@ -127,7 +128,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
runnable = ResetToFactorySettingsTask(), runnable = ResetToFactorySettingsTask(),
cardId = cardId, cardId = cardId,
initialMessage = Message(context.getString(R.string.card_settings_reset_card_to_factory)), initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
) )
.map { CardDTO(it) } .map { CardDTO(it) }
} }
@ -154,7 +155,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changePasscode(null), SetUserCodeCommand.changePasscode(null),
cardId, cardId,
initialMessage = Message(context.getString(R.string.initial_message_change_passcode_body)), initialMessage = Message(resources.getString(R.string.initial_message_change_passcode_body)),
) )
} }
@ -162,7 +163,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
SetUserCodeCommand.changeAccessCode(null), SetUserCodeCommand.changeAccessCode(null),
cardId, cardId,
initialMessage = Message(context.getString(R.string.initial_message_change_access_code_body)), initialMessage = Message(resources.getString(R.string.initial_message_change_access_code_body)),
) )
} }
@ -170,15 +171,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
SetUserCodeCommand.resetUserCodes(), SetUserCodeCommand.resetUserCodes(),
cardId, cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)), initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
)
}
suspend fun checkUserCodes(cardId: String?): CompletionResult<CheckUserCodesResponse> {
return runTaskAsyncReturnOnMain(
CheckUserCodesCommand(),
cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)),
) )
} }
@ -186,7 +179,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
SetUserCodeRecoveryAllowedTask(enabled), SetUserCodeRecoveryAllowedTask(enabled),
cardId, cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)), initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
) )
} }
@ -197,7 +190,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return runTaskAsyncReturnOnMain( return runTaskAsyncReturnOnMain(
runnable = ScanTask(allowRequestAccessCodeFromRepository), runnable = ScanTask(allowRequestAccessCodeFromRepository),
cardId = cardId, cardId = cardId,
initialMessage = Message(context.getString(R.string.initial_message_tap_header)), initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
) )
.map { CardDTO(it) } .map { CardDTO(it) }
} }
@ -233,8 +226,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
} }
} }
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String { fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
return context.getString(stringResId, *formatArgs) return resources.getString(stringResId, *formatArgs)
} }
fun setAccessCodeRequestPolicy(useBiometricsForAccessCode: Boolean) { fun setAccessCodeRequestPolicy(useBiometricsForAccessCode: Boolean) {

View file

@ -1,24 +1,17 @@
package com.tangem.tap.domain.extensions package com.tangem.tap.domain.extensions
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result import com.tangem.common.services.Result
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.getTwinCardNumber import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.Artwork
import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.CardVerifyAndGetInfo
import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.features.wallet.redux.Artwork
val CardDTO.remainingSignatures: Int? val CardDTO.remainingSignatures: Int?
get() = this.wallets.firstOrNull()?.remainingSignatures get() = this.wallets.firstOrNull()?.remainingSignatures
val CardDTO.isWalletDataSupported: Boolean
get() = this.firmwareVersion.major >= 4
val CardDTO.isFirmwareMultiwalletAllowed: Boolean
get() = firmwareVersion >= FirmwareVersion.MultiWalletAvailable && settings.maxWalletsCount > 1
val CardDTO.isHdWalletAllowedByApp: Boolean val CardDTO.isHdWalletAllowedByApp: Boolean
get() = settings.isHDWalletAllowed get() = settings.isHDWalletAllowed
@ -58,16 +51,4 @@ suspend fun CardDTO.getOrLoadCardArtworkUrl(cardInfo: Result<CardVerifyAndGetInf
is Result.Failure -> ifAnyError() is Result.Failure -> ifAnyError()
} }
}
fun CardDTO.getArtworkUrl(artworkId: String?): String? {
return when {
artworkId != null -> {
OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> null
}
} }

View file

@ -31,7 +31,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val seedKey = wallet.extendedPublicKey val seedKey = wallet.extendedPublicKey
return when { return when {
scanResponse.cardTypesResolver.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> { scanResponse.cardTypesResolver.isTangemTwins() && scanResponse.secondTwinPublicKey != null -> {
makeTwinWalletManager( createTwinWalletManager(
walletPublicKey = wallet.publicKey, walletPublicKey = wallet.publicKey,
pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(), pairPublicKey = scanResponse.secondTwinPublicKey!!.hexToBytes(),
blockchain = environmentBlockchain, blockchain = environmentBlockchain,
@ -47,7 +47,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
val derivedKey = derivedKeys?.get(derivationPath) val derivedKey = derivedKeys?.get(derivationPath)
?: return null ?: return null
makeWalletManager( createWalletManager(
blockchain = environmentBlockchain, blockchain = environmentBlockchain,
seedKey = wallet.publicKey, seedKey = wallet.publicKey,
derivedKey = derivedKey, derivedKey = derivedKey,
@ -55,7 +55,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
) )
} }
else -> { else -> {
makeWalletManager( createLegacyWalletManager(
blockchain = environmentBlockchain, blockchain = environmentBlockchain,
walletPublicKey = wallet.publicKey, walletPublicKey = wallet.publicKey,
curve = wallet.curve, curve = wallet.curve,

View file

@ -2,21 +2,22 @@ package com.tangem.tap.domain.scanCard
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError import com.tangem.common.core.TangemError
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store import com.tangem.tap.store
// TODO: Remove this object after feature toggle was removed and use ScanCardUseCase instead // TODO: Remove this object after feature toggle was removed and use ScanCardUseCase instead
internal object ScanCardProcessor { internal class DefaultScanCardProcessor : ScanCardProcessor {
private val isNewCardScanningEnabled: Boolean private val isNewCardScanningEnabled: Boolean
get() = store.state.daggerGraphState get() = store.state.daggerGraphState
.get(DaggerGraphState::customTokenFeatureToggles) .get(DaggerGraphState::customTokenFeatureToggles)
.isNewCardScanningEnabled .isNewCardScanningEnabled
suspend fun scan( override suspend fun scan(
cardId: String? = null, cardId: String?,
allowsRequestAccessCodeFromRepository: Boolean = false, allowsRequestAccessCodeFromRepository: Boolean,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
return if (isNewCardScanningEnabled) { return if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository) UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
@ -26,15 +27,15 @@ internal object ScanCardProcessor {
} }
@Suppress("LongParameterList") @Suppress("LongParameterList")
suspend fun scan( override suspend fun scan(
analyticsEvent: AnalyticsEvent? = null, analyticsEvent: AnalyticsEvent?,
cardId: String? = null, cardId: String?,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {}, onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
onScanStateChange: suspend (scanInProgress: Boolean) -> Unit = {}, onScanStateChange: suspend (scanInProgress: Boolean) -> Unit,
onWalletNotCreated: suspend () -> Unit = {}, onWalletNotCreated: suspend () -> Unit,
disclaimerWillShow: () -> Unit = {}, disclaimerWillShow: () -> Unit,
onFailure: suspend (error: TangemError) -> Unit = {}, onFailure: suspend (error: TangemError) -> Unit,
onSuccess: suspend (scanResponse: ScanResponse) -> Unit = {}, onSuccess: suspend (scanResponse: ScanResponse) -> Unit,
) { ) {
if (isNewCardScanningEnabled) { if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan( UseCaseScanProcessor.scan(

View file

@ -6,7 +6,7 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess import com.tangem.common.doOnSuccess
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.TapWorkarounds.canSkipBackup

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard
import arrow.fx.coroutines.resourceScope import arrow.fx.coroutines.resourceScope
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemError import com.tangem.common.core.TangemError
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.card.ScanCardException import com.tangem.domain.card.ScanCardException

View file

@ -3,7 +3,7 @@ package com.tangem.tap.domain.scanCard.chains
import arrow.core.Either import arrow.core.Either
import arrow.core.right import arrow.core.right
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.card.ScanCardException import com.tangem.domain.card.ScanCardException
import com.tangem.domain.core.chain.Chain import com.tangem.domain.core.chain.Chain
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse

View file

@ -31,8 +31,7 @@ class ResetToFactorySettingsTask : CardSessionRunnable<Card> {
} }
private fun resetBackup(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) { private fun resetBackup(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
val backupStatus = session.environment.card?.backupStatus if (session.environment.card?.backupStatus?.isActive != true) {
if (backupStatus == null || backupStatus == Card.BackupStatus.NoBackup) {
callback(CompletionResult.Success(session.environment.card!!)) callback(CompletionResult.Success(session.environment.card!!))
return return
} }

View file

@ -10,7 +10,7 @@ import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.datasource.files.AndroidFileReader import com.tangem.datasource.files.AndroidFileReader
import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.domain.tokens.converters.CurrencyConverter import com.tangem.tap.domain.tokens.converters.CurrencyConverter
import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency

View file

@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.EllipticCurve import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.guard import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.analytics.events.WalletConnect import com.tangem.tap.common.analytics.events.WalletConnect
@ -36,7 +37,6 @@ import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -56,8 +56,9 @@ class WalletConnectManager {
.addInterceptor(RetryInterceptor()) .addInterceptor(RetryInterceptor())
.build() .build()
} }
private val interceptor by lazy { private val interceptor by lazy {
HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } createNetworkLoggingInterceptor()
} }
private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf() private var sessions: MutableMap<Topic, WalletConnectActiveData> = mutableMapOf()

View file

@ -6,6 +6,7 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak
import com.tangem.blockchain.common.* import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.extensions.hexToBigDecimal import com.tangem.blockchain.extensions.hexToBigDecimal
@ -35,8 +36,8 @@ import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTrans
import com.tangem.tap.features.details.redux.walletconnect.* import com.tangem.tap.features.details.redux.walletconnect.*
import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData
import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import com.tangem.tap.tangemSdkManager import com.tangem.tap.tangemSdkManager
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage.WCSignType.* import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage.WCSignType.*
import timber.log.Timber import timber.log.Timber
@ -76,7 +77,8 @@ class WalletConnectSdkHelper {
val transactionData = TransactionData( val transactionData = TransactionData(
amount = Amount(value, wallet.blockchain), amount = Amount(value, wallet.blockchain),
fee = Amount(fee, wallet.blockchain), // TODO refactoring
fee = Fee.Common(Amount(fee, wallet.blockchain)),
sourceAddress = transaction.from, sourceAddress = transaction.from,
destinationAddress = transaction.to!!, destinationAddress = transaction.to!!,
extras = EthereumTransactionExtras( extras = EthereumTransactionExtras(
@ -161,7 +163,10 @@ class WalletConnectSdkHelper {
private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? { private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? {
val result = (data.walletManager as TransactionSender).send( val result = (data.walletManager as TransactionSender).send(
transactionData = data.transaction, transactionData = data.transaction,
signer = CommonSigner(tangemSdk, cardId), signer = CommonSigner(
tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk,
cardId = cardId,
),
) )
return when (result) { return when (result) {
SimpleResult.Success -> { SimpleResult.Success -> {
@ -182,7 +187,6 @@ class WalletConnectSdkHelper {
transactionData = data.transaction, transactionData = data.transaction,
nonce = null, nonce = null,
blockchain = data.walletManager.wallet.blockchain, blockchain = data.walletManager.wallet.blockchain,
gasLimit = null,
) ?: return null ) ?: return null
val command = SignHashCommand( val command = SignHashCommand(

View file

@ -6,6 +6,8 @@ import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.TransactionSigner
import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.toBlockchainSdkError import com.tangem.blockchain.common.toBlockchainSdkError
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.CompletionResult import com.tangem.common.CompletionResult
@ -84,13 +86,13 @@ object DemoHelper {
class DemoTransactionSender(private val walletManager: WalletManager) : TransactionSender { class DemoTransactionSender(private val walletManager: WalletManager) : TransactionSender {
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> { override suspend fun getFee(amount: Amount, destination: String): Result<TransactionFee> {
val blockchain = walletManager.wallet.blockchain val blockchain = walletManager.wallet.blockchain
return Result.Success( return Result.Success(
listOf( TransactionFee.Choosable(
Amount(0.0001.toBigDecimal(), blockchain), minimum = Fee.Common(Amount(minimumFee, blockchain)),
Amount(0.0002.toBigDecimal(), blockchain), normal = Fee.Common(Amount(normalFee, blockchain)),
Amount(0.0003.toBigDecimal(), blockchain), priority = Fee.Common(Amount(priorityFee, blockchain)),
), ),
) )
} }
@ -120,5 +122,9 @@ class DemoTransactionSender(private val walletManager: WalletManager) : Transact
companion object { companion object {
val ID = DemoTransactionSender::class.java.simpleName val ID = DemoTransactionSender::class.java.simpleName
private val minimumFee = 0.0001.toBigDecimal()
private val normalFee = 0.0002.toBigDecimal()
private val priorityFee = 0.0003.toBigDecimal()
} }
} }

View file

@ -12,6 +12,8 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -23,9 +25,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
import com.tangem.tap.domain.userWalletList.isLockedSync import com.tangem.tap.domain.userWalletList.isLockedSync
@ -33,6 +32,7 @@ import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.tangemSdkManager import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -74,7 +74,8 @@ class DetailsMiddleware {
is DetailsAction.AccessCodeRecovery -> accessCodeRecoveryMiddleware.handle(state, action) is DetailsAction.AccessCodeRecovery -> accessCodeRecoveryMiddleware.handle(state, action)
DetailsAction.ScanCard -> { DetailsAction.ScanCard -> {
scope.launch { scope.launch {
ScanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true) store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor)
.scan(allowsRequestAccessCodeFromRepository = true)
.doOnSuccess { scanResponse -> .doOnSuccess { scanResponse ->
// if we use biometric, scanResponse in GlobalState is null, and crashes NPE on twin cards // if we use biometric, scanResponse in GlobalState is null, and crashes NPE on twin cards
store.dispatch(GlobalAction.SaveScanResponse(scanResponse)) store.dispatch(GlobalAction.SaveScanResponse(scanResponse))
@ -371,9 +372,9 @@ class DetailsMiddleware {
Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.On)) Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.On))
preferencesStorage.shouldSaveAccessCodes = true preferencesStorage.shouldSaveAccessCodes = true
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState
useBiometricsForAccessCode = scanResponse?.card?.isAccessCodeSet == true, .get(DaggerGraphState::cardSdkConfigRepository)
) .setAccessCodeRequestPolicy(isBiometricsRequestPolicy = scanResponse?.card?.isAccessCodeSet == true)
return CompletionResult.Success(Unit) return CompletionResult.Success(Unit)
} }
@ -384,9 +385,9 @@ class DetailsMiddleware {
Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.Off)) Analytics.send(Settings.AppSettings.SaveAccessCodeSwitcherChanged(AnalyticsParam.OnOffState.Off))
preferencesStorage.shouldSaveAccessCodes = false preferencesStorage.shouldSaveAccessCodes = false
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState
useBiometricsForAccessCode = false, .get(DaggerGraphState::cardSdkConfigRepository)
) .setAccessCodeRequestPolicy(isBiometricsRequestPolicy = false)
} }
.doOnFailure { error -> .doOnFailure { error ->
Timber.e(error, "Unable to delete saved access codes") Timber.e(error, "Unable to delete saved access codes")

View file

@ -11,6 +11,7 @@ import androidx.activity.OnBackPressedCallback
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.zxing.BarcodeFormat
import com.google.zxing.Result import com.google.zxing.Result
import com.otaliastudios.cameraview.CameraView import com.otaliastudios.cameraview.CameraView
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
@ -38,10 +39,19 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (!permissionIsGranted()) requestPermission() if (!permissionIsGranted()) requestPermission()
scannerView = ZXingScannerView(activity) scannerView = ZXingScannerView(activity).apply {
setFormats(listOf(BarcodeFormat.QR_CODE))
}
return scannerView return scannerView
} }
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
scannerView?.stopCamera() scannerView?.stopCamera()
@ -52,12 +62,6 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
setFitSystemWindows(fit = false) setFitSystemWindows(fit = false)
} }
override fun onResume() {
super.onResume()
scannerView?.setResultHandler(this)
scannerView?.startCamera()
}
override fun handleResult(result: Result) { override fun handleResult(result: Result) {
store.dispatch(NavigationAction.PopBackTo()) store.dispatch(NavigationAction.PopBackTo())
setFitSystemWindows(fit = false) setFitSystemWindows(fit = false)

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.home.redux package com.tangem.tap.features.home.redux
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.entities.IndeterminateProgressButton

View file

@ -5,11 +5,11 @@ import com.tangem.common.doOnResult
import com.tangem.common.doOnSuccess import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.guard import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.* import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.analytics.events.Shop
@ -20,13 +20,16 @@ import com.tangem.tap.common.extensions.eraseContext
import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.features.home.BELARUS_COUNTRY_CODE import com.tangem.tap.features.home.BELARUS_COUNTRY_CODE
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.home.redux.HomeMiddleware.BUY_WALLET_URL import com.tangem.tap.features.home.redux.HomeMiddleware.BUY_WALLET_URL
import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.ButtonState
import com.tangem.tap.features.signin.redux.SignInAction import com.tangem.tap.features.signin.redux.SignInAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.rekotlin.Action import org.rekotlin.Action
@ -74,11 +77,11 @@ private fun handleHomeAction(action: Action) {
private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch { private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
delay(timeMillis = 200) delay(timeMillis = 200)
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
) )
ScanCardProcessor.scan( store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = analyticsEvent, analyticsEvent = analyticsEvent,
onProgressStateChange = { showProgress -> onProgressStateChange = { showProgress ->
if (showProgress) { if (showProgress) {

View file

@ -11,12 +11,12 @@ import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.extensions.removeContext import com.tangem.tap.common.extensions.removeContext
import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View file

@ -9,6 +9,7 @@ import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.analytics.events.Onboarding
@ -19,7 +20,6 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makePrimaryWalletManager import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.domain.userWalletList.isLockedSync import com.tangem.tap.domain.userWalletList.isLockedSync
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE

View file

@ -17,15 +17,12 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.ShareElement import com.tangem.core.navigation.ShareElement
import com.tangem.core.ui.fragments.setStatusBarColor import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.asset.AssetReader
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork
import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.TwinsCardWidget import com.tangem.tap.domain.twins.TwinsCardWidget
@ -35,7 +32,6 @@ import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletM
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding import com.tangem.wallet.databinding.LayoutOnboardingContainerTopBinding

View file

@ -14,6 +14,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.Artwork
import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.data.model.CreateWalletResponse
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
import com.tangem.operations.backup.BackupService import com.tangem.operations.backup.BackupService
@ -30,10 +31,9 @@ import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.wallet.models.toCurrencies import com.tangem.tap.features.wallet.models.toCurrencies
import com.tangem.tap.features.wallet.redux.Artwork import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.rekotlin.Action import org.rekotlin.Action
import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware import org.rekotlin.Middleware
object OnboardingWalletMiddleware { object OnboardingWalletMiddleware {
@ -44,8 +44,8 @@ private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state
{ next -> { next ->
{ action -> { action ->
when (action) { when (action) {
is OnboardingWallet2Action -> handleWallet2Action(action, state) is OnboardingWallet2Action -> handleWallet2Action(action)
else -> handleWalletAction(action, state, dispatch) else -> handleWalletAction(action)
} }
next(action) next(action)
} }
@ -53,7 +53,7 @@ private val onboardingWalletMiddleware: Middleware<AppState> = { dispatch, state
} }
@Suppress("LongMethod", "ComplexMethod") @Suppress("LongMethod", "ComplexMethod")
private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch: DispatchFunction) { private fun handleWalletAction(action: Action) {
if (action !is OnboardingWalletAction) return if (action !is OnboardingWalletAction) return
val globalState = store.state.globalState val globalState = store.state.globalState
@ -179,7 +179,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
} }
@Suppress("LongMethod", "ComplexMethod") @Suppress("LongMethod", "ComplexMethod")
private fun handleWallet2Action(action: OnboardingWallet2Action, state: () -> AppState?) { private fun handleWallet2Action(action: OnboardingWallet2Action) {
val globalState = store.state.globalState val globalState = store.state.globalState
val onboardingManager = globalState.onboardingState.onboardingManager val onboardingManager = globalState.onboardingState.onboardingManager
@ -330,7 +330,9 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
is BackupAction.AddBackupCard -> { is BackupAction.AddBackupCard -> {
backupService.addBackupCard { result -> backupService.addBackupCard { result ->
backupService.skipCompatibilityChecks = false backupService.skipCompatibilityChecks = false
tangemSdk.config.filter.cardIdFilter = null store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk
.config.filter.cardIdFilter = null
when (result) { when (result) {
is CompletionResult.Success -> { is CompletionResult.Success -> {
store.dispatchOnMain(BackupAction.AddBackupCard.Success) store.dispatchOnMain(BackupAction.AddBackupCard.Success)

View file

@ -9,6 +9,7 @@ import com.tangem.common.flatMap
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -18,10 +19,10 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
import com.tangem.tap.domain.userWalletList.isLockable import com.tangem.tap.domain.userWalletList.isLockable
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.rekotlin.Middleware import org.rekotlin.Middleware
import timber.log.Timber import timber.log.Timber
@ -110,9 +111,10 @@ internal class SaveWalletMiddleware {
// Enable saving access codes only if this is the first time user save the wallet // Enable saving access codes only if this is the first time user save the wallet
if (isFirstSavedWallet) { if (isFirstSavedWallet) {
preferencesStorage.shouldSaveAccessCodes = true preferencesStorage.shouldSaveAccessCodes = true
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository)
useBiometricsForAccessCode = userWallet.hasAccessCode, .setAccessCodeRequestPolicy(
) isBiometricsRequestPolicy = userWallet.hasAccessCode,
)
} }
val savedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { val savedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {

View file

@ -4,6 +4,7 @@ import com.tangem.Message
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered
import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.StateDialog
@ -114,7 +115,7 @@ sealed class FeeAction : SendScreenAction {
object RequestFee : FeeAction() object RequestFee : FeeAction()
sealed class FeeCalculation : FeeAction() { sealed class FeeCalculation : FeeAction() {
data class SetFeeResult(val fee: List<Amount>) : FeeCalculation() data class SetFeeResult(val fee: TransactionFee) : FeeCalculation()
object ClearResult : FeeCalculation() object ClearResult : FeeCalculation()
} }

View file

@ -67,7 +67,7 @@ class AmountMiddleware {
} }
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto)) val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto))
val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee) val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee?.amount)
val amountFieldErrors = filterErrorsForAmountField(transactionErrors) val amountFieldErrors = filterErrorsForAmountField(transactionErrors)
if (amountFieldErrors.isEmpty()) { if (amountFieldErrors.isEmpty()) {
dispatch(AmountAction.SetAmountError(null)) dispatch(AmountAction.SetAmountError(null))

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionSender import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero import com.tangem.common.extensions.isZero
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
@ -53,15 +54,18 @@ class RequestFeeMiddleware {
val result = feeResult.data val result = feeResult.data
// val result = FeeMock.getFee(walletManager.wallet.blockchain) // val result = FeeMock.getFee(walletManager.wallet.blockchain)
dispatch(FeeAction.FeeCalculation.SetFeeResult(result)) dispatch(FeeAction.FeeCalculation.SetFeeResult(result))
if (result.size == 1) { when (result) {
val fee = result[0].value ?: BigDecimal.ZERO is TransactionFee.Single -> {
if (fee.isZero()) { val fee = result.normal.amount.value ?: BigDecimal.ZERO
dispatch(FeeAction.ChangeLayoutVisibility(main = false)) if (fee.isZero()) {
} else { dispatch(FeeAction.ChangeLayoutVisibility(main = false))
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false)) } else {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false))
}
}
is TransactionFee.Choosable -> {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true))
} }
} else {
dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true))
} }
} }
is Result.Failure -> { is Result.Failure -> {

View file

@ -7,13 +7,14 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder.XrpTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder.XrpTransactionExtras
import com.tangem.core.navigation.NavigationAction
import com.tangem.blockchain.common.* import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard import com.tangem.common.extensions.guard
import com.tangem.common.services.Result import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
@ -38,6 +39,7 @@ import com.tangem.tap.features.send.redux.FeeAction.RequestFee
import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.wallet.R import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -97,11 +99,11 @@ private fun verifyAndSendTransaction(
val card = appState.globalState.scanResponse?.card ?: return val card = appState.globalState.scanResponse?.card ?: return
val destinationAddress = sendState.addressState.destinationWalletAddress ?: return val destinationAddress = sendState.addressState.destinationWalletAddress ?: return
val typedAmount = sendState.amountState.amountToExtract ?: return val typedAmount = sendState.amountState.amountToExtract ?: return
val feeAmount = sendState.feeState.currentFee ?: return val fee = sendState.feeState.currentFee ?: return
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend()) val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend())
val transactionErrors = walletManager.validateTransaction(amountToSend, feeAmount) val transactionErrors = walletManager.validateTransaction(amountToSend, fee.amount)
when { when {
transactionErrors.contains(TransactionError.TezosSendAll) -> { transactionErrors.contains(TransactionError.TezosSendAll) -> {
val reduceAmount = walletManager.wallet.blockchain.minimalAmount() val reduceAmount = walletManager.wallet.blockchain.minimalAmount()
@ -116,7 +118,7 @@ private fun verifyAndSendTransaction(
action = action, action = action,
walletManager = walletManager, walletManager = walletManager,
amountToSend = amountToSend, amountToSend = amountToSend,
feeAmount = feeAmount, fee = fee,
feeType = sendState.feeState.selectedFeeType, feeType = sendState.feeState.selectedFeeType,
destinationAddress = destinationAddress, destinationAddress = destinationAddress,
transactionExtras = sendState.transactionExtrasState, transactionExtras = sendState.transactionExtrasState,
@ -135,7 +137,7 @@ private fun verifyAndSendTransaction(
action = action, action = action,
walletManager = walletManager, walletManager = walletManager,
amountToSend = amountToSend, amountToSend = amountToSend,
feeAmount = feeAmount, fee = fee,
feeType = sendState.feeState.selectedFeeType, feeType = sendState.feeState.selectedFeeType,
destinationAddress = destinationAddress, destinationAddress = destinationAddress,
transactionExtras = sendState.transactionExtrasState, transactionExtras = sendState.transactionExtrasState,
@ -153,7 +155,7 @@ private fun sendTransaction(
action: SendActionUi.SendAmountToRecipient, action: SendActionUi.SendAmountToRecipient,
walletManager: WalletManager, walletManager: WalletManager,
amountToSend: Amount, amountToSend: Amount,
feeAmount: Amount, fee: Fee,
feeType: FeeType, feeType: FeeType,
destinationAddress: String, destinationAddress: String,
transactionExtras: TransactionExtrasState, transactionExtras: TransactionExtrasState,
@ -163,7 +165,7 @@ private fun sendTransaction(
dispatch: (Action) -> Unit, dispatch: (Action) -> Unit,
) { ) {
dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS)) dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS))
var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress) var txData = walletManager.createTransaction(amountToSend, fee, destinationAddress)
transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) } transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) }
transactionExtras.binanceMemo?.memo?.let { txData = txData.copy(extras = BinanceTransactionExtras(it.toString())) } transactionExtras.binanceMemo?.memo?.let { txData = txData.copy(extras = BinanceTransactionExtras(it.toString())) }
@ -181,7 +183,7 @@ private fun sendTransaction(
updateFeedbackManagerInfo( updateFeedbackManagerInfo(
walletManager = walletManager, walletManager = walletManager,
amountToSend = amountToSend, amountToSend = amountToSend,
feeAmount = feeAmount, feeAmount = fee.amount,
destinationAddress = destinationAddress, destinationAddress = destinationAddress,
) )
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error = error)) dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error = error))
@ -200,6 +202,7 @@ private fun sendTransaction(
return@launch return@launch
} }
val tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk
val linkedTerminalState = tangemSdk.config.linkedTerminal val linkedTerminalState = tangemSdk.config.linkedTerminal
if (card.isStart2Coin) { if (card.isStart2Coin) {
tangemSdk.config.linkedTerminal = false tangemSdk.config.linkedTerminal = false
@ -276,7 +279,7 @@ private fun sendTransaction(
updateFeedbackManagerInfo( updateFeedbackManagerInfo(
walletManager = walletManager, walletManager = walletManager,
amountToSend = amountToSend, amountToSend = amountToSend,
feeAmount = feeAmount, feeAmount = fee.amount,
destinationAddress = destinationAddress, destinationAddress = destinationAddress,
) )
val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux.reducers package com.tangem.tap.features.send.redux.reducers
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.FeeActionUi import com.tangem.tap.features.send.redux.FeeActionUi
import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.SendScreenAction
@ -13,6 +14,7 @@ import com.tangem.tap.features.wallet.redux.ProgressState
[REDACTED_AUTHOR] [REDACTED_AUTHOR]
*/ */
class FeeReducer : SendInternalReducer { class FeeReducer : SendInternalReducer {
override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) {
is FeeActionUi -> handleUiAction(action, sendState, sendState.feeState) is FeeActionUi -> handleUiAction(action, sendState, sendState.feeState)
is FeeAction -> handleAction(action, sendState, sendState.feeState) is FeeAction -> handleAction(action, sendState, sendState.feeState)
@ -25,7 +27,9 @@ class FeeReducer : SendInternalReducer {
state.copy(controlsLayoutIsVisible = !state.controlsLayoutIsVisible) state.copy(controlsLayoutIsVisible = !state.controlsLayoutIsVisible)
} }
is FeeActionUi.ChangeSelectedFee -> { is FeeActionUi.ChangeSelectedFee -> {
val currentFee = createValueOfFeeAmount(action.feeType, state.feeList) val currentFee = state.fees?.let {
createValueOfFeeAmount(action.feeType, it)
}
state.copy( state.copy(
selectedFeeType = action.feeType, selectedFeeType = action.feeType,
currentFee = currentFee, currentFee = currentFee,
@ -50,34 +54,36 @@ class FeeReducer : SendInternalReducer {
) )
} }
is FeeAction.FeeCalculation.SetFeeResult -> { is FeeAction.FeeCalculation.SetFeeResult -> {
val fees = action.fee when (val fees = action.fee) {
if (fees.size == 1) { is TransactionFee.Single -> {
val feeType = FeeType.SINGLE val feeType = FeeType.SINGLE
val currentFee = createValueOfFeeAmount(feeType, fees) val currentFee = createValueOfFeeAmount(feeType, fees)
state.copy( state.copy(
selectedFeeType = feeType, selectedFeeType = feeType,
feeList = fees, fees = fees,
currentFee = currentFee, currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState), feeIsApproximate = isFeeApproximate(sendState),
) )
} else { }
val feeType = getCurrentFeeType(state) is TransactionFee.Choosable -> {
val currentFee = createValueOfFeeAmount(feeType, fees) val feeType = getCurrentFeeType(state)
val currentFee = createValueOfFeeAmount(feeType, fees)
state.copy( state.copy(
selectedFeeType = feeType, selectedFeeType = feeType,
feeList = fees, fees = fees,
currentFee = currentFee, currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState), feeIsApproximate = isFeeApproximate(sendState),
) )
}
}.copy( }.copy(
progressState = ProgressState.Done, progressState = ProgressState.Done,
) )
} }
FeeAction.FeeCalculation.ClearResult -> { FeeAction.FeeCalculation.ClearResult -> {
state.copy( state.copy(
feeList = null, fees = null,
currentFee = null, currentFee = null,
progressState = ProgressState.Done, progressState = ProgressState.Done,
) )
@ -87,17 +93,18 @@ class FeeReducer : SendInternalReducer {
return updateLastState(sendState.copy(feeState = result), result) return updateLastState(sendState.copy(feeState = result), result)
} }
private fun createValueOfFeeAmount(feeType: FeeType, list: List<Amount>?): Amount? { private fun createValueOfFeeAmount(feeType: FeeType, transactionFee: TransactionFee): Fee {
if (list == null || list.isEmpty()) return null return when (transactionFee) {
is TransactionFee.Single -> {
return if (list.size == 1) { transactionFee.normal
list[0] }
} else { is TransactionFee.Choosable -> {
when (feeType) { when (feeType) {
FeeType.SINGLE -> list[1] FeeType.SINGLE -> transactionFee.normal
FeeType.LOW -> list[0] FeeType.LOW -> transactionFee.minimum
FeeType.NORMAL -> list[1] FeeType.NORMAL -> transactionFee.normal
FeeType.PRIORITY -> list[2] FeeType.PRIORITY -> transactionFee.priority
}
} }
} }
} }

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux.states package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.ProgressState
import java.math.BigDecimal import java.math.BigDecimal
@ -23,8 +24,8 @@ fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType {
data class FeeState( data class FeeState(
val selectedFeeType: FeeType = FeeType.NORMAL, val selectedFeeType: FeeType = FeeType.NORMAL,
val feeList: List<Amount>? = null, val fees: TransactionFee? = null,
val currentFee: Amount? = null, val currentFee: Fee? = null,
val feeIsIncluded: Boolean = false, val feeIsIncluded: Boolean = false,
val feeIsApproximate: Boolean = false, val feeIsApproximate: Boolean = false,
val mainLayoutIsVisible: Boolean = false, val mainLayoutIsVisible: Boolean = false,
@ -38,5 +39,5 @@ data class FeeState(
fun isReady(): Boolean = currentFee != null fun isReady(): Boolean = currentFee != null
fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO fun getCurrentFeeValue(): BigDecimal = currentFee?.amount?.value ?: BigDecimal.ZERO
} }

View file

@ -11,6 +11,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.transition.TransitionInflater import androidx.transition.TransitionInflater
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.tokens.impl.presentation.ui.TokensListScreen import com.tangem.tap.features.tokens.impl.presentation.ui.TokensListScreen
import com.tangem.tap.features.tokens.impl.presentation.viewmodels.TokensListViewModel import com.tangem.tap.features.tokens.impl.presentation.viewmodels.TokensListViewModel
@ -40,6 +41,10 @@ internal class TokensListFragment : Fragment() {
} }
TangemTheme { TangemTheme {
val statusBarColor = TangemTheme.colors.background.secondary
SystemBarsEffect {
setSystemBarsColor(color = statusBarColor)
}
TokensListScreen( TokensListScreen(
modifier = Modifier.systemBarsPadding(), modifier = Modifier.systemBarsPadding(),
stateHolder = viewModel.uiState, stateHolder = viewModel.uiState,

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux
import android.content.Context import android.content.Context
import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet

View file

@ -1,11 +1,11 @@
package com.tangem.tap.features.wallet.redux package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManager
import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.userwallets.Artwork
import com.tangem.tap.common.entities.Button import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.common.toggleWidget.WidgetState
@ -118,19 +118,4 @@ enum class ErrorType {
sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { sealed class WalletMainButton(enabled: Boolean) : Button(enabled) {
class SendButton(enabled: Boolean) : WalletMainButton(enabled) class SendButton(enabled: Boolean) : WalletMainButton(enabled)
}
data class Artwork(
val artworkId: String,
val artwork: Bitmap? = null,
) {
companion object {
const val DEFAULT_IMG_URL = "https://app.tangem.com/cards/card_default.png"
const val SERGIO_CARD_URL = "https://app.tangem.com/cards/card_tg059.png"
const val MARTA_CARD_URL = "https://app.tangem.com/cards/card_tg083.png"
const val SERGIO_CARD_ID = "BC01"
const val MARTA_CARD_ID = "BC02"
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
}
} }

View file

@ -15,10 +15,10 @@ import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.scope import com.tangem.tap.scope
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.tap.userWalletsListManager import com.tangem.tap.userWalletsListManager
@ -102,7 +102,8 @@ class MultiWalletMiddleware {
} }
private fun scanAndUpdateCard(selectedUserWallet: UserWallet) = scope.launch(Dispatchers.Default) { private fun scanAndUpdateCard(selectedUserWallet: UserWallet) = scope.launch(Dispatchers.Default) {
ScanCardProcessor.scan(selectedUserWallet.cardId, allowsRequestAccessCodeFromRepository = true) store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor)
.scan(cardId = selectedUserWallet.cardId, allowsRequestAccessCodeFromRepository = true)
.flatMap { scanResponse -> .flatMap { scanResponse ->
userWalletsListManager.update( userWalletsListManager.update(
userWalletId = selectedUserWallet.walletId, userWalletId = selectedUserWallet.walletId,

View file

@ -72,7 +72,7 @@ class TradeCryptoMiddleware {
} }
scope.launch { scope.launch {
exchangeManager.buyErc20TestnetTokens( buyErc20TestnetTokens(
card = card, card = card,
walletManager = walletManager, walletManager = walletManager,
token = currency.token, token = currency.token,

View file

@ -12,6 +12,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.userwallets.GetCardImageUseCase
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
@ -23,7 +24,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.userWalletList.GetCardImageUseCase
import com.tangem.tap.domain.userWalletList.lockIfLockable import com.tangem.tap.domain.userWalletList.lockIfLockable
import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeAction

View file

@ -1,14 +1,19 @@
package com.tangem.tap.features.wallet.redux.reducers package com.tangem.tap.features.wallet.redux.reducers
import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.address.AddressType
import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.userwallets.Artwork
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.userWalletsListManager import com.tangem.tap.userWalletsListManager
import org.rekotlin.Action import org.rekotlin.Action
@ -114,7 +119,7 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
if (selectedUserWallet == action.userWalletId) { if (selectedUserWallet == action.userWalletId) {
newState = newState.copy( newState = newState.copy(
cardImage = Artwork(artworkId = action.url, artwork = newState.cardImage?.artwork), cardImage = Artwork(artworkId = action.url),
) )
} }
} }
@ -151,7 +156,7 @@ fun Wallet.createAddressesData(): List<WalletDataModel.AddressData> {
getShareUri(it.value), getShareUri(it.value),
getExploreUrl(it.value), getExploreUrl(it.value),
) )
if (it.type == blockchain.defaultAddressType()) { if (it.type == AddressType.Default) {
listOfAddressData.add(0, addressData) listOfAddressData.add(0, addressData)
} else { } else {
listOfAddressData.add(addressData) listOfAddressData.add(addressData)

View file

@ -4,7 +4,7 @@ import androidx.annotation.IdRes
import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.wallet.R import com.tangem.wallet.R
@ -102,8 +102,8 @@ class BalanceWidget(
groupBaseCurrency.show() groupBaseCurrency.show()
tvCurrency.text = tokenWalletData?.currency?.currencySymbol tvCurrency.text = tokenWalletData?.currency?.currencySymbol
tvBaseCurrency.text = data.currency.currencyName tvBaseCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) tokenWalletData?.getFormattedAmount() else "" tvAmount.text = if (showAmount) tokenWalletData?.getFormattedCryptoAmount() else ""
tvBaseAmount.text = if (showAmount) data.getFormattedAmount() else "" tvBaseAmount.text = if (showAmount) data.getFormattedCryptoAmount() else ""
if (showAmount) { if (showAmount) {
tvFiatAmount.show() tvFiatAmount.show()
tvFiatAmount.text = tokenWalletData?.getFormattedFiatAmount(store.state.globalState.appCurrency) tvFiatAmount.text = tokenWalletData?.getFormattedFiatAmount(store.state.globalState.appCurrency)
@ -113,7 +113,7 @@ class BalanceWidget(
private fun showBalanceWithoutToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) { private fun showBalanceWithoutToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) {
groupBaseCurrency.hide() groupBaseCurrency.hide()
tvCurrency.text = data.currency.currencyName tvCurrency.text = data.currency.currencyName
tvAmount.text = if (showAmount) data.getFormattedAmount() else "" tvAmount.text = if (showAmount) data.getFormattedCryptoAmount() else ""
if (showAmount) { if (showAmount) {
tvFiatAmount.show() tvFiatAmount.show()
tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency) tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency)

View file

@ -1,44 +1,38 @@
package com.tangem.tap.features.wallet.ui package com.tangem.tap.features.wallet.ui
import android.view.View import android.view.View
import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressType
import com.tangem.blockchain.blockchains.cardano.CardanoAddressType
import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.address.AddressType
import com.tangem.wallet.R import com.tangem.wallet.R
object MultipleAddressUiHelper { object MultipleAddressUiHelper {
fun typeToId(type: AddressType): Int {
return when (type) { private val blockchainsSupportingSplit = listOf(
is BitcoinAddressType.Legacy -> R.id.chip_legacy Blockchain.Bitcoin,
is BitcoinAddressType.Segwit -> R.id.chip_default Blockchain.BitcoinTestnet,
is CardanoAddressType.Byron -> R.id.chip_legacy Blockchain.Litecoin,
is CardanoAddressType.Shelley -> R.id.chip_default Blockchain.BitcoinCash,
else -> View.NO_ID Blockchain.CardanoShelley,
)
fun typeToId(type: AddressType, blockchain: Blockchain): Int {
return if (blockchain in blockchainsSupportingSplit) {
if (type == AddressType.Legacy) {
R.id.chip_legacy
} else {
R.id.chip_default
}
} else {
View.NO_ID
} }
} }
fun idToType(id: Int, blockchain: Blockchain?): AddressType? { fun idToType(id: Int, blockchain: Blockchain): AddressType? {
return when (id) { return when (blockchain) {
R.id.chip_default -> { in blockchainsSupportingSplit -> {
when (blockchain) { when (id) {
Blockchain.Bitcoin, R.id.chip_default -> AddressType.Default
Blockchain.BitcoinTestnet, R.id.chip_legacy -> AddressType.Legacy
Blockchain.Litecoin,
Blockchain.BitcoinCash,
-> BitcoinAddressType.Segwit
Blockchain.CardanoShelley -> CardanoAddressType.Shelley
else -> null
}
}
R.id.chip_legacy -> {
when (blockchain) {
Blockchain.Bitcoin,
Blockchain.BitcoinTestnet,
Blockchain.Litecoin,
Blockchain.BitcoinCash,
-> BitcoinAddressType.Legacy
Blockchain.CardanoShelley -> CardanoAddressType.Byron
else -> null else -> null
} }
} }

View file

@ -59,7 +59,7 @@ import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWallet import com.tangem.tap.features.wallet.ui.test.TestWallet
import com.tangem.tap.features.wallet.ui.utils.assembleWarnings import com.tangem.tap.features.wallet.ui.utils.assembleWarnings
import com.tangem.tap.features.wallet.ui.utils.getAvailableActions import com.tangem.tap.features.wallet.ui.utils.getAvailableActions
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy
import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell
@ -390,7 +390,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
chipGroupAddressType.show() chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth() chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(selectedAddress.type) val checkedId = MultipleAddressUiHelper.typeToId(selectedAddress.type, currency.blockchain)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId) if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chipGroupAddressType.setOnCheckedChangeListener { _, checkedId -> chipGroupAddressType.setOnCheckedChangeListener { _, checkedId ->
@ -425,7 +425,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
lBalance.root.show() lBalance.root.show()
lBalance.groupBalance.show() lBalance.groupBalance.show()
lBalance.tvError.hide() lBalance.tvError.hide()
lBalance.tvAmount.text = walletData.getFormattedAmount() lBalance.tvAmount.text = walletData.getFormattedCryptoAmount()
lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading) lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading)
} }
@ -437,7 +437,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
lBalance.root.show() lBalance.root.show()
lBalance.groupBalance.show() lBalance.groupBalance.show()
lBalance.tvError.hide() lBalance.tvError.hide()
lBalance.tvAmount.text = walletData.getFormattedAmount() lBalance.tvAmount.text = walletData.getFormattedCryptoAmount()
lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency)
when (status) { when (status) {
is WalletDataModel.VerifiedOnline, is WalletDataModel.VerifiedOnline,

View file

@ -15,7 +15,7 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.utils.getFormattedAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount
import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatRate import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatRate
import com.tangem.tap.store import com.tangem.tap.store
@ -85,7 +85,7 @@ class WalletAdapter : ListAdapter<WalletDataModel, WalletAdapter.WalletsViewHold
lContent.tvCurrency.text = wallet.currency.currencyName lContent.tvCurrency.text = wallet.currency.currencyName
lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency) lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency)
lContent.tvAmount.text = wallet.getFormattedAmount() lContent.tvAmount.text = wallet.getFormattedCryptoAmount()
lContent.tvStatus.isVisible = statusMessage != null lContent.tvStatus.isVisible = statusMessage != null
lContent.tvStatus.text = statusMessage lContent.tvStatus.text = statusMessage

View file

@ -8,7 +8,7 @@ import com.tangem.feature.swap.domain.SwapInteractor
import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.toFiatRateString import com.tangem.tap.common.extensions.toFiatRateString
import com.tangem.tap.common.extensions.toFiatValue import com.tangem.tap.common.extensions.toFiatValue
import com.tangem.tap.common.extensions.toFormattedCurrencyString import com.tangem.tap.common.extensions.toFormattedCryptoCurrencyString
import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
@ -38,8 +38,8 @@ internal fun WalletDataModel.hasPendingTransactions(): Boolean {
return status.pendingTransactions.isEmpty() return status.pendingTransactions.isEmpty()
} }
internal fun WalletDataModel.getFormattedAmount(): String { internal fun WalletDataModel.getFormattedCryptoAmount(): String {
return status.amount.toFormattedCurrencyString( return status.amount.toFormattedCryptoCurrencyString(
decimals = currency.decimals, decimals = currency.decimals,
currency = currency.currencySymbol, currency = currency.currencySymbol,
) )

View file

@ -190,7 +190,10 @@ class SingleWalletView : WalletView() {
(binding.lAddress.root as? ViewGroup)?.beginDelayedTransition() (binding.lAddress.root as? ViewGroup)?.beginDelayedTransition()
chipGroupAddressType.show() chipGroupAddressType.show()
chipGroupAddressType.fitChipsByGroupWidth() chipGroupAddressType.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(primaryWallet.walletAddresses.selectedAddress.type) val checkedId = MultipleAddressUiHelper.typeToId(
primaryWallet.walletAddresses.selectedAddress.type,
primaryWallet.currency.blockchain,
)
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId) if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
chipGroupAddressType.setOnCheckedChangeListener { group, checkedId -> chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->

View file

@ -6,6 +6,8 @@ import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.* import com.tangem.tap.*
@ -18,10 +20,8 @@ import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.TotalFiatBalance import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
@ -126,14 +126,16 @@ internal class WalletSelectorMiddleware {
private fun addWallet() = scope.launch { private fun addWallet() = scope.launch {
Analytics.send(MyWallets.Button.ScanNewCard()) Analytics.send(MyWallets.Button.ScanNewCard())
val prevUseBiometricsForAccessCode = tangemSdkManager.useBiometricsForAccessCode() val cardSdkConfigRepository = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository)
val prevUseBiometricsForAccessCode = cardSdkConfigRepository.isBiometricsRequestPolicy()
// Update access code policy for access code saving when a card was scanned // Update access code policy for access code saving when a card was scanned
tangemSdkManager.setAccessCodeRequestPolicy( cardSdkConfigRepository.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
) )
ScanCardProcessor.scan( store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets), analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets),
onWalletNotCreated = { onWalletNotCreated = {
// No need to rollback policy, continue with the policy set before the card scan // No need to rollback policy, continue with the policy set before the card scan
@ -147,14 +149,14 @@ internal class WalletSelectorMiddleware {
saveUserWalletAndPopBackToWalletScreen(scanResponse) saveUserWalletAndPopBackToWalletScreen(scanResponse)
.doOnFailure { error -> .doOnFailure { error ->
// Rollback policy if card saving was failed // Rollback policy if card saving was failed
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode) cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
Timber.e(error, "Unable to save user wallet") Timber.e(error, "Unable to save user wallet")
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
} }
}, },
onFailure = { error -> onFailure = { error ->
// Rollback policy if card scanning was failed // Rollback policy if card scanning was failed
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode) cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
Timber.e(error, "Unable to scan card") Timber.e(error, "Unable to scan card")
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
}, },
@ -208,7 +210,7 @@ internal class WalletSelectorMiddleware {
private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult<Unit> { private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult<Unit> {
Analytics.send(MyWallets.Button.WalletUnlockTapped()) Analytics.send(MyWallets.Button.WalletUnlockTapped())
tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse) tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse)
return ScanCardProcessor.scan() return store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan()
.map { scanResponse -> .map { scanResponse ->
val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
if (scannedUserWalletId == userWallet.walletId) { if (scannedUserWalletId == userWallet.walletId) {

View file

@ -8,6 +8,7 @@ import com.tangem.common.flatMap
import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.userwallets.UserWalletBuilder
import com.tangem.tap.* import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Basic
@ -15,11 +16,10 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.AppState
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.domain.userWalletList.unlockIfLockable
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.signin.redux.SignInAction import com.tangem.tap.features.signin.redux.SignInAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.rekotlin.Middleware import org.rekotlin.Middleware
import timber.log.Timber import timber.log.Timber
@ -100,10 +100,10 @@ internal class WelcomeMiddleware {
} }
private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) { private suspend inline fun scanCardInternal(crossinline onCardScanned: suspend (ScanResponse) -> Unit) {
tangemSdkManager.setAccessCodeRequestPolicy( store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
) )
ScanCardProcessor.scan( store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn), analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn),
onSuccess = { scanResponse -> onSuccess = { scanResponse ->
scope.launch { onCardScanned(scanResponse) } scope.launch { onCardScanned(scanResponse) }

View file

@ -13,8 +13,8 @@ import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import java.math.BigDecimal import java.math.BigDecimal
/** /**
@ -72,7 +72,7 @@ class CurrencyExchangeManager(
return when (action) { return when (action) {
Action.Buy -> buyService Action.Buy -> buyService
Action.Sell -> sellService Action.Sell -> sellService
} as ExchangeUrlBuilder }
} }
enum class Action { Buy, Sell } enum class Action { Buy, Sell }
@ -86,31 +86,26 @@ class CurrencyExchangeManager(
} }
} }
suspend fun CurrencyExchangeManager.buyErc20TestnetTokens( suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletManager, token: Token) {
card: CardDTO,
walletManager: EthereumWalletManager,
token: Token,
) {
walletManager.safeUpdate() walletManager.safeUpdate()
val amountToSend = Amount(walletManager.wallet.blockchain) val amountToSend = Amount(walletManager.wallet.blockchain)
val destinationAddress = token.contractAddress val destinationAddress = token.contractAddress
val feeResult = val feeResult = walletManager.getFee(
walletManager.getFee( amountToSend,
amountToSend, destinationAddress,
destinationAddress, ) as? Result.Success ?: return
) as? Result.Success ?: return val fee = feeResult.data.minimum
val fee = feeResult.data[0]
val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
if (coinValue < fee.value) return if (coinValue < fee.amount.value) return
val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress) val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress)
val signer = TangemSigner( val signer = TangemSigner(
card = card, card = card,
tangemSdk = tangemSdk, tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk,
initialMessage = Message(), initialMessage = Message(),
) { signResponse -> ) { signResponse ->
store.dispatch( store.dispatch(

View file

@ -1,6 +1,5 @@
package com.tangem.tap.proxy package com.tangem.tap.proxy
import com.tangem.TangemSdk
import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.NavigationStateHolder import com.tangem.core.navigation.NavigationStateHolder
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
@ -41,7 +40,6 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationState
var userTokensRepository: UserTokensRepository? = null var userTokensRepository: UserTokensRepository? = null
var mainStore: Store<AppState>? = null var mainStore: Store<AppState>? = null
var tangemSdkManager: TangemSdkManager? = null var tangemSdkManager: TangemSdkManager? = null
var tangemSdk: TangemSdk? = null
var walletStoresManager: WalletStoresManager? = null var walletStoresManager: WalletStoresManager? = null
var appFiatCurrency: FiatCurrency = FiatCurrency.Default var appFiatCurrency: FiatCurrency = FiatCurrency.Default

View file

@ -6,12 +6,15 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.blockchains.optimism.OptimismWalletManager import com.tangem.blockchain.blockchains.optimism.OptimismWalletManager
import com.tangem.blockchain.common.* import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.SimpleResult import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.blockchain.extensions.isNetworkError import com.tangem.blockchain.extensions.isNetworkError
import com.tangem.common.core.TangemSdkError import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.hexToBytes
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.TransactionManager
@ -22,7 +25,6 @@ import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType
import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TangemSigner import com.tangem.tap.domain.TangemSigner
import com.tangem.tap.tangemSdk
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger import java.math.BigInteger
import java.math.MathContext import java.math.MathContext
@ -32,6 +34,7 @@ import java.math.RoundingMode
class TransactionManagerImpl( class TransactionManagerImpl(
private val appStateHolder: AppStateHolder, private val appStateHolder: AppStateHolder,
private val analytics: AnalyticsEventHandler, private val analytics: AnalyticsEventHandler,
private val cardSdkConfigRepository: CardSdkConfigRepository,
) : TransactionManager { ) : TransactionManager {
override suspend fun sendApproveTransaction( override suspend fun sendApproveTransaction(
@ -112,7 +115,7 @@ class TransactionManagerImpl(
): SendTxResult { ): SendTxResult {
val txData = walletManager.createTransaction( val txData = walletManager.createTransaction(
amount = amount, amount = amount,
fee = Amount(value = feeAmount, blockchain = blockchain), fee = Fee.Common(Amount(value = feeAmount, blockchain = blockchain)),
destination = destinationAddress, destination = destinationAddress,
).copy(hash = dataToSign, extras = createExtras(walletManager, gasLimit, dataToSign)) ).copy(hash = dataToSign, extras = createExtras(walletManager, gasLimit, dataToSign))
@ -214,24 +217,37 @@ class TransactionManagerImpl(
return when (fee) { return when (fee) {
is Result.Success -> { is Result.Success -> {
// for not EVM blockchains set gasLimit ZERO for now // for not EVM blockchains set gasLimit ZERO for now
val firstFee = fee.data.firstOrNull() ?: error("no fee found") when (fee.data) {
val minFee = ProxyFee( is TransactionFee.Single -> {
gasLimit = BigInteger.ZERO, val fee = (fee.data as TransactionFee.Single).normal
fee = convertToProxyAmount(amount = firstFee), val singleFee = ProxyFee(
) gasLimit = BigInteger.ZERO,
val normalFee = ProxyFee( fee = convertToProxyAmount(amount = fee.amount),
gasLimit = BigInteger.ZERO, )
fee = convertToProxyAmount(fee.data.getOrNull(index = 1) ?: firstFee), ProxyFees(
) minFee = singleFee,
val priorityFee = ProxyFee( normalFee = singleFee,
gasLimit = BigInteger.ZERO, priorityFee = singleFee,
fee = convertToProxyAmount(fee.data.getOrNull(index = 2) ?: firstFee), )
) }
ProxyFees( is TransactionFee.Choosable -> {
minFee = minFee, val choosableFee = fee.data as TransactionFee.Choosable
normalFee = normalFee, ProxyFees(
priorityFee = priorityFee, minFee = ProxyFee(
) gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.minimum.amount),
),
normalFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.normal.amount),
),
priorityFee = ProxyFee(
gasLimit = BigInteger.ZERO,
fee = convertToProxyAmount(amount = choosableFee.priority.amount),
),
)
}
}
} }
is Result.Failure -> { is Result.Failure -> {
error(fee.error.message ?: fee.error.customMessage) error(fee.error.message ?: fee.error.customMessage)
@ -280,19 +296,21 @@ class TransactionManagerImpl(
} }
return when (fee) { return when (fee) {
is Result.Success -> { is Result.Success -> {
val minFee = fee.data.firstOrNull() ?: error("no fee found") val choosableFee = fee.data
val minProxyFee = ProxyFee( val minProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO, gasLimit = (choosableFee.minimum as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(minFee), fee = convertToProxyAmount(amount = choosableFee.minimum.amount),
) )
val normalProxyFee = ProxyFee( val normalProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO, gasLimit = (choosableFee.normal as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(fee.data.getOrNull(index = 1) ?: minFee), fee = convertToProxyAmount(amount = choosableFee.normal.amount),
) )
val priorityProxyFee = ProxyFee( val priorityProxyFee = ProxyFee(
gasLimit = walletManager.gasLimit ?: BigInteger.ZERO, gasLimit = (choosableFee.priority as Fee.Ethereum).gasLimit,
fee = convertToProxyAmount(fee.data.lastOrNull() ?: minFee), fee = convertToProxyAmount(amount = choosableFee.priority.amount),
) )
ProxyFees( ProxyFees(
minFee = minProxyFee, minFee = minProxyFee,
normalFee = normalProxyFee, normalFee = normalProxyFee,
@ -378,7 +396,7 @@ class TransactionManagerImpl(
val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" } val actualCard = requireNotNull(appStateHolder.getActualCard()) { "no card found" }
return TangemSigner( return TangemSigner(
card = actualCard, card = actualCard,
tangemSdk = tangemSdk, tangemSdk = cardSdkConfigRepository.sdk,
initialMessage = Message(), initialMessage = Message(),
) { signResponse -> ) { signResponse ->
appStateHolder.mainStore?.dispatch( appStateHolder.mainStore?.dispatch(

View file

@ -10,6 +10,7 @@ import com.tangem.domain.common.BlockchainNetwork
import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toCoinId
import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.userwallets.UserWalletIdBuilder
import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.UserWalletManager
import com.tangem.lib.crypto.models.Currency import com.tangem.lib.crypto.models.Currency
import com.tangem.lib.crypto.models.Currency.NativeToken import com.tangem.lib.crypto.models.Currency.NativeToken
@ -17,7 +18,6 @@ import com.tangem.lib.crypto.models.Currency.NonNativeToken
import com.tangem.lib.crypto.models.ProxyAmount import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.lib.crypto.models.ProxyFiatCurrency import com.tangem.lib.crypto.models.ProxyFiatCurrency
import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.userWalletsListManager import com.tangem.tap.userWalletsListManager
import com.tangem.tap.walletCurrenciesManager import com.tangem.tap.walletCurrenciesManager

View file

@ -2,6 +2,7 @@ package com.tangem.tap.proxy.di
import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.intl.Locale
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider
@ -43,8 +44,13 @@ class ProxyModule {
fun provideTransactionManager( fun provideTransactionManager(
appStateHolder: AppStateHolder, appStateHolder: AppStateHolder,
analytics: AnalyticsEventHandler, analytics: AnalyticsEventHandler,
cardSdkConfigRepository: CardSdkConfigRepository,
): TransactionManager { ): TransactionManager {
return TransactionManagerImpl(appStateHolder, analytics) return TransactionManagerImpl(
appStateHolder = appStateHolder,
analytics = analytics,
cardSdkConfigRepository = cardSdkConfigRepository,
)
} }
@Provides @Provides

View file

@ -1,6 +1,7 @@
package com.tangem.tap.proxy.redux package com.tangem.tap.proxy.redux
import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.features.wallet.navigation.WalletRouter
@ -15,5 +16,6 @@ sealed interface DaggerGraphAction : Action {
val walletRouter: WalletRouter, val walletRouter: WalletRouter,
val walletConnectInteractor: WalletConnectInteractor, val walletConnectInteractor: WalletConnectInteractor,
val tokenDetailsRouter: TokenDetailsRouter, val tokenDetailsRouter: TokenDetailsRouter,
val cardSdkConfigRepository: CardSdkConfigRepository,
) : DaggerGraphAction ) : DaggerGraphAction
} }

View file

@ -18,6 +18,7 @@ object DaggerGraphReducer {
walletRouter = action.walletRouter, walletRouter = action.walletRouter,
walletConnectInteractor = action.walletConnectInteractor, walletConnectInteractor = action.walletConnectInteractor,
tokenDetailsRouter = action.tokenDetailsRouter, tokenDetailsRouter = action.tokenDetailsRouter,
cardSdkConfigRepository = action.cardSdkConfigRepository,
) )
} }
} }

View file

@ -2,7 +2,9 @@ package com.tangem.tap.proxy.redux
import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.asset.AssetReader
import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
@ -27,6 +29,8 @@ data class DaggerGraphState(
val walletConnectInteractor: WalletConnectInteractor? = null, val walletConnectInteractor: WalletConnectInteractor? = null,
val tokenDetailsFeatureToggles: TokenDetailsFeatureToggles? = null, val tokenDetailsFeatureToggles: TokenDetailsFeatureToggles? = null,
val tokenDetailsRouter: TokenDetailsRouter? = null, val tokenDetailsRouter: TokenDetailsRouter? = null,
val scanCardProcessor: ScanCardProcessor? = null,
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
) : StateType { ) : StateType {
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T { inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {

Binary file not shown.

View file

@ -13,5 +13,7 @@ dependencies {
kapt(deps.hilt.kapt) kapt(deps.hilt.kapt)
/** Core shouldn't depends on core, but in case with utils and logging its necessary */ /** Core shouldn't depends on core, but in case with utils and logging its necessary */
implementation(project(":core:utils")) implementation(projects.core.utils)
implementation(projects.core.analytics.models)
} }

1
core/analytics/models/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,4 @@
plugins {
alias(deps.plugins.kotlin.jvm)
id("configuration")
}

View file

@ -1,4 +1,4 @@
package com.tangem.core.analytics package com.tangem.core.analytics.models
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,18 +1,9 @@
package com.tangem.core.analytics package com.tangem.core.analytics
import com.tangem.core.analytics.api.AnalyticsEventFilter import com.tangem.core.analytics.api.*
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsFilterHolder
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandlerHolder
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.api.ParamsInterceptorHolder
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import java.util.concurrent.Executors import java.util.concurrent.Executors
/** /**

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -1,6 +1,6 @@
package com.tangem.core.analytics.api package com.tangem.core.analytics.api
import com.tangem.core.analytics.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsEvent
/** /**
[REDACTED_AUTHOR] [REDACTED_AUTHOR]

View file

@ -32,7 +32,7 @@ dependencies {
implementation(deps.moshi) implementation(deps.moshi)
implementation(deps.moshi.kotlin) implementation(deps.moshi.kotlin)
implementation(deps.okHttp) implementation(deps.okHttp)
implementation(deps.okHttp.logging) implementation(deps.okHttp.prettyLogging)
implementation(deps.retrofit) implementation(deps.retrofit)
implementation(deps.retrofit.moshi) implementation(deps.retrofit.moshi)
implementation(deps.reactive.network) implementation(deps.reactive.network)

View file

@ -1,8 +1,10 @@
package com.tangem.datasource.api.common package com.tangem.datasource.api.common
import android.util.Log
import com.ihsanbal.logging.Level
import com.ihsanbal.logging.LoggingInterceptor
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit import retrofit2.Retrofit
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -22,7 +24,7 @@ fun createRetrofitInstance(
} }
interceptors.forEach { okHttpBuilder.addInterceptor(it) } interceptors.forEach { okHttpBuilder.addInterceptor(it) }
if (logEnabled) okHttpBuilder.addInterceptor(createHttpLoggingInterceptor()) if (logEnabled) okHttpBuilder.addInterceptor(createNetworkLoggingInterceptor())
return Retrofit.Builder() return Retrofit.Builder()
.baseUrl(baseUrl) .baseUrl(baseUrl)
@ -31,6 +33,9 @@ fun createRetrofitInstance(
.build() .build()
} }
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply { fun createNetworkLoggingInterceptor(): Interceptor {
level = HttpLoggingInterceptor.Level.BODY return LoggingInterceptor.Builder()
.setLevel(Level.BODY)
.log(Log.VERBOSE)
.build()
} }

View file

@ -0,0 +1,20 @@
package com.tangem.datasource.di
import com.tangem.datasource.local.cache.CacheKeysStore
import com.tangem.datasource.local.cache.RuntimeCacheKeysStore
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 CacheKeysStoreModule {
@Provides
@Singleton
fun provideCacheKeysStore(): CacheKeysStore {
return RuntimeCacheKeysStore()
}
}

View file

@ -63,9 +63,7 @@ class NetworkModule {
@PromotionOneInch @PromotionOneInch
fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi { fun providePromotionOneInchApi(authProvider: AuthProvider, @NetworkMoshi moshi: Moshi): PromotionApi {
val okClient = OkHttpClient.Builder() val okClient = OkHttpClient.Builder()
.addHeaders( .addHeaders(AuthenticationHeader(authProvider))
AuthenticationHeader(authProvider),
)
.allowLogging() .allowLogging()
.callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) .callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS) .connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)

View file

@ -0,0 +1,14 @@
package com.tangem.datasource.local.cache
import com.tangem.datasource.local.cache.model.CacheKey
interface CacheKeysStore {
fun get(id: String): CacheKey?
fun addOrReplace(key: CacheKey)
fun remove(id: String)
fun clear()
}

Some files were not shown because too many files have changed in this diff Show more