diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index ac3eeb6f5d..8f2af51f2d 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -14,6 +14,7 @@
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1c3f8708a3..7500382879 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -9,6 +9,7 @@ plugins {
alias(deps.plugins.google.services)
alias(deps.plugins.hilt.android)
alias(deps.plugins.firebase.crashlytics)
+ alias(deps.plugins.firebase.perf)
id("configuration")
}
@@ -213,7 +214,10 @@ dependencies {
implementation(deps.firebase.analytics)
implementation(deps.firebase.crashlytics)
implementation(deps.firebase.messaging)
-
+ implementation(deps.firebase.perf) {
+ exclude(group = "com.google.firebase", module = "protolite-well-known-types")
+ exclude(group = "com.google.protobuf", module = "protobuf-javalite")
+ }
/** Tangem libraries */
implementation(deps.tangem.blockchain) {
exclude(module = "joda-time")
diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt
index 328a4fe0f8..d28fb50cbc 100644
--- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt
+++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt
@@ -8,6 +8,8 @@ import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+import com.tangem.core.decompose.di.GlobalUiMessageSender
+import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
@@ -134,4 +136,7 @@ interface ApplicationEntryPoint {
fun getSettingsManager(): SettingsManager
fun getBlockchainExceptionHandler(): BlockchainExceptionHandler
+
+ @GlobalUiMessageSender
+ fun getUiMessageSender(): UiMessageSender
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt
index 3819915526..d54f1c4c78 100644
--- a/app/src/main/java/com/tangem/tap/TangemApplication.kt
+++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt
@@ -6,7 +6,6 @@ import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.tangem.Log
import com.tangem.TangemSdkLogger
-import com.tangem.blockchain.common.ExceptionHandler
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
@@ -21,6 +20,7 @@ import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.Basic.TransactionSent.WalletForm
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.datasource.api.common.MoshiConverter
@@ -53,7 +53,6 @@ import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
-import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
import com.tangem.tap.common.images.createCoilImageLoader
@@ -208,8 +207,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
private val settingsManager: SettingsManager
get() = entryPoint.getSettingsManager()
- private val blockchainExceptionHandler: BlockchainExceptionHandler
- get() = entryPoint.getBlockchainExceptionHandler()
+ private val uiMessageSender: UiMessageSender
+ get() = entryPoint.getUiMessageSender()
+
// endregion
override fun onCreate() {
@@ -252,7 +252,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
}
loadNativeLibraries()
- ExceptionHandler.append(blockchainExceptionHandler)
+ // ExceptionHandler.append(blockchainExceptionHandler) // TODO [REDACTED_TASK_KEY] Send only to Firebase
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
@@ -308,6 +308,7 @@ abstract class TangemApplication : Application(), ImageLoaderFactory {
appPreferencesStore = appPreferencesStore,
clipboardManager = clipboardManager,
settingsManager = settingsManager,
+ uiMessageSender = uiMessageSender,
),
),
)
diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
index b4df44bdff..da4a494936 100644
--- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt
+++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt
@@ -58,10 +58,6 @@ class DialogManager : StoreSubscriber {
)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
- is AppDialog.RussianCardholdersWarningDialog -> RussianCardholdersWarningBottomSheetDialog(
- context,
- state.dialog.data,
- )
is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context)
is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
is OnboardingDialog.WalletActivationError -> WalletActivationErrorDialog.create(context, state.dialog)
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt
index e1dfdea4c1..f35a636601 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt
@@ -13,7 +13,6 @@ sealed class Onboarding(
class Started : Onboarding("Onboarding", "Onboarding Started")
class Finished : Onboarding("Onboarding", "Onboarding Finished")
- data object OfflineAttestationFailed : Onboarding(category = "Onboarding", event = "Offline Attestation Failed")
sealed class CreateWallet(
event: String,
diff --git a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
index c80f34373b..9bdca56b66 100644
--- a/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
+++ b/app/src/main/java/com/tangem/tap/common/pushes/TangemPushNotificationService.kt
@@ -38,7 +38,7 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
// TODO refactoring: [REDACTED_JIRA]
val intent = Intent(applicationContext, MainActivity::class.java)
- intent.putExtra(OnPushClickedIntentHandler.IS_OPENED_FROM_PUSH, true)
+ intent.putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(
/* context = */ this,
diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
index 4586881efc..1188cd7d03 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt
@@ -27,10 +27,6 @@ sealed class AppDialog : StateDialog {
val actionsList: List,
) : AppDialog()
- data class RussianCardholdersWarningDialog(val data: Data?) : AppDialog() {
- data class Data(val topUpUrl: String)
- }
-
data class RemoveWalletDialog(
val currencyTitle: String,
val onOk: () -> Unit,
diff --git a/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt
index 18882c44dd..df54352380 100644
--- a/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt
+++ b/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt
@@ -67,7 +67,7 @@ internal class AddressInfoBottomSheetDialog(
val blockchain = stateDialog.currency.blockchain
tvReceiveMessage.text = tvReceiveMessage.getString(
id = R.string.address_qr_code_message_format,
- blockchain.fullName,
+ blockchain.getCoinName(),
blockchain.currency,
blockchain.fullName,
)
diff --git a/app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt
deleted file mode 100644
index 5b0ac2e000..0000000000
--- a/app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.tangem.tap.common.ui
-
-import android.content.Context
-import android.os.Bundle
-import android.view.LayoutInflater
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.tangem.core.analytics.Analytics
-import com.tangem.tap.common.analytics.events.Token
-import com.tangem.tap.common.extensions.dispatchDialogHide
-import com.tangem.tap.common.extensions.dispatchOpenUrl
-import com.tangem.tap.common.redux.AppDialog
-import com.tangem.tap.store
-import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding
-
-class RussianCardholdersWarningBottomSheetDialog(
- context: Context,
- private val dialogData: AppDialog.RussianCardholdersWarningDialog.Data?,
-) : BottomSheetDialog(context) {
-
- private var binding: DialogRussiansCardholdersWarningBinding? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- Analytics.send(Token.Topup.P2PScreenOpened())
- binding = DialogRussiansCardholdersWarningBinding
- .inflate(LayoutInflater.from(context))
- .also { setContentView(it.root) }
- }
-
- override fun show() {
- super.show()
- setOnDismissListener {
- binding = null
- store.dispatchDialogHide()
- }
-
- binding?.btnYes?.setOnClickListener {
- if (dialogData != null) {
- store.dispatchOpenUrl(dialogData.topUpUrl)
- Analytics.send(Token.Topup.ScreenOpened())
- }
-
- dismiss()
- }
- binding?.btnNo?.setOnClickListener {
- store.dispatchOpenUrl(INSTRUCTION_URL)
- dismiss()
- }
- }
-
- companion object {
- private const val INSTRUCTION_URL = "https://tangem.com/howtobuy.html"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt
index a7592add78..216af3ca6d 100644
--- a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt
+++ b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt
@@ -19,11 +19,13 @@ internal class DefaultScanCardProcessor(
override suspend fun scan(
cardId: String?,
allowsRequestAccessCodeFromRepository: Boolean,
+ analyticsSource: AnalyticsParam.ScreensSources,
): CompletionResult {
return if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
} else {
legacyScanProcessor.scan(
+ analyticsSource = analyticsSource,
cardId = cardId,
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
)
diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
index 96137b75ea..27fa8ead9e 100644
--- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
+++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt
@@ -11,13 +11,13 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
+import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.toWrappedList
-import com.tangem.core.ui.message.DialogMessage
-import com.tangem.core.ui.message.EventMessageAction
+import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull
@@ -28,7 +28,6 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.sdk.extensions.localizedDescriptionRes
-import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppDialog
@@ -60,13 +59,14 @@ internal class LegacyScanProcessor @Inject constructor(
suspend fun scan(
cardId: String? = null,
allowsRequestAccessCodeFromRepository: Boolean = false,
+ analyticsSource: AnalyticsParam.ScreensSources,
): CompletionResult {
return tangemSdkManager.scanProduct(
cardId = cardId,
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
)
.doOnFailure { error ->
- onScanFailure(error = error, onFailure = {}, onCancel = {})
+ onScanFailure(analyticsSource = analyticsSource, error = error, onFailure = {}, onCancel = {})
}
}
@@ -93,6 +93,7 @@ internal class LegacyScanProcessor @Inject constructor(
result
.doOnFailure { error ->
onScanFailure(
+ analyticsSource = analyticsSource,
error = error,
onFailure = onFailure,
onCancel = {
@@ -163,41 +164,35 @@ internal class LegacyScanProcessor @Inject constructor(
}
private suspend inline fun onScanFailure(
+ analyticsSource: AnalyticsParam.ScreensSources,
error: TangemError,
crossinline onFailure: suspend (TangemError) -> Unit,
crossinline onCancel: () -> Unit,
) {
if (error is TangemSdkError.CardVerificationFailed) {
- analyticsEventHandler.send(event = Onboarding.OfflineAttestationFailed)
+ analyticsEventHandler.send(
+ event = OnboardingAnalyticsEvent.Onboarding.OfflineAttestationFailed(
+ analyticsSource,
+ ),
+ )
val resource = error.localizedDescriptionRes()
val resId = resource.resId ?: R.string.common_unknown_error
val resArgs = resource.args.map { it.value }
uiMessageSender.send(
- message = DialogMessage(
- message = resourceReference(id = resId, resArgs.toWrappedList()),
- title = resourceReference(id = R.string.security_alert_title),
- isDismissable = false,
- firstActionBuilder = {
- EventMessageAction(
- title = resourceReference(id = R.string.alert_button_request_support),
- onClick = {
- mainScope.launch {
- onCancel()
+ message = Dialogs.cardVerificationFailed(
+ errorDescription = resourceReference(id = resId, resArgs.toWrappedList()),
+ onRequestSupport = {
+ mainScope.launch {
+ onCancel()
- store.inject(DaggerGraphState::sendFeedbackEmailUseCase).invoke(
- type = FeedbackEmailType.CardAttestationFailed,
- )
- }
- },
- )
- },
- secondActionBuilder = {
- cancelAction {
- mainScope.launch { onCancel() }
+ store.inject(DaggerGraphState::sendFeedbackEmailUseCase).invoke(
+ type = FeedbackEmailType.CardAttestationFailed,
+ )
}
},
+ onCancelClick = { onCancel() },
),
)
} else {
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
index 35d535aa53..b7b896682c 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt
@@ -4,7 +4,9 @@ import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.CompletionCallback
import com.tangem.domain.common.TwinsHelper
+import com.tangem.operations.attestation.AttestationTask
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
import com.tangem.operations.wallet.PurgeWalletCommand
@@ -13,7 +15,7 @@ class CreateFirstTwinWalletTask(private val firstCardId: String) : CardSessionRu
override val allowsRequestAccessCodeFromRepository: Boolean = false
- override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) {
+ override fun run(session: CardSession, callback: CompletionCallback) {
val card = session.environment.card
val publicKey = card?.wallets?.firstOrNull()?.publicKey
if (publicKey != null) {
@@ -29,13 +31,40 @@ class CreateFirstTwinWalletTask(private val firstCardId: String) : CardSessionRu
when (response) {
is CompletionResult.Success -> {
session.environment.card = session.environment.card?.setWallets(emptyList())
- CreateWalletTask(EllipticCurve.Secp256k1).run(session) { callback(it) }
+ createWallet(session, callback)
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(response.error))
}
}
} else {
- CreateWalletTask(EllipticCurve.Secp256k1).run(session) { callback(it) }
+ createWallet(session, callback)
+ }
+ }
+
+ private fun createWallet(session: CardSession, callback: CompletionCallback) {
+ CreateWalletTask(EllipticCurve.Secp256k1).run(session) { createWalletResponse ->
+ when (createWalletResponse) {
+ is CompletionResult.Success -> {
+ runAttestation(session) { attestationResponse ->
+ when (attestationResponse) {
+ is CompletionResult.Success -> callback(createWalletResponse)
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(attestationResponse.error))
+ }
+ }
+ }
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(createWalletResponse.error))
+ }
+ }
+ }
+
+ companion object {
+
+ internal fun runAttestation(session: CardSession, callback: CompletionCallback<*>) {
+ val mode = session.environment.config.attestationMode
+ val secureStorage = session.environment.secureStorage
+ val attestationTask = AttestationTask(mode, secureStorage)
+
+ attestationTask.run(session, callback)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
index d91c57c086..d55d7d7d32 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt
@@ -6,11 +6,13 @@ import com.tangem.common.KeyPair
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
+import com.tangem.common.core.CompletionCallback
import com.tangem.common.extensions.hexToBytes
import com.tangem.domain.common.TwinsHelper
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.CreateWalletTask
import com.tangem.operations.wallet.PurgeWalletCommand
+import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask.Companion.runAttestation
class CreateSecondTwinWalletTask(
private val firstPublicKey: String,
@@ -59,12 +61,10 @@ class CreateSecondTwinWalletTask(
CreateWalletTask(EllipticCurve.Secp256k1).run(session) { result ->
when (result) {
is CompletionResult.Success -> {
- session.environment.card = session.environment.card?.updateWallet(result.data.wallet)
-
- WriteProtectedIssuerDataTask(firstPublicKey.hexToBytes(), issuerKeys).run(session) { writeResult ->
- when (writeResult) {
- is CompletionResult.Success -> callback(result)
- is CompletionResult.Failure -> callback(CompletionResult.Failure(writeResult.error))
+ runAttestation(session) { attestationResponse ->
+ when (attestationResponse) {
+ is CompletionResult.Success -> writeProtectedIssuerData(session, result, callback)
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(attestationResponse.error))
}
}
}
@@ -72,4 +72,19 @@ class CreateSecondTwinWalletTask(
}
}
}
+
+ private fun writeProtectedIssuerData(
+ session: CardSession,
+ result: CompletionResult.Success,
+ callback: CompletionCallback,
+ ) {
+ session.environment.card = session.environment.card?.updateWallet(result.data.wallet)
+
+ WriteProtectedIssuerDataTask(firstPublicKey.hexToBytes(), issuerKeys).run(session) { writeResult ->
+ when (writeResult) {
+ is CompletionResult.Success -> callback(result)
+ is CompletionResult.Failure -> callback(CompletionResult.Failure(writeResult.error))
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
index beaf919a98..2a9dcc2d23 100644
--- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt
@@ -1,7 +1,6 @@
package com.tangem.tap.domain.twins
import com.tangem.Message
-import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.KeyPair
import com.tangem.common.extensions.hexToBytes
@@ -57,7 +56,7 @@ class TwinCardsManager(card: CardDTO) {
return response
}
- suspend fun complete(message: Message): Result {
+ suspend fun complete(message: Message): CompletionResult {
val response = tangemSdkManager.finalizeTwin(
secondCardPublicKey = secondCardPublicKey!!.hexToBytes(),
issuerKeyPair = getIssuerKeys(),
@@ -65,10 +64,7 @@ class TwinCardsManager(card: CardDTO) {
initialMessage = message,
)
- return when (response) {
- is CompletionResult.Success -> Result.Success(response.data)
- is CompletionResult.Failure -> Result.fromTangemSdkError(response.error)
- }
+ return response
}
private suspend fun getIssuerKeys(): KeyPair {
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt
index 776836a294..d49c3386d7 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/DefaultLegacyWalletConnectRepository.kt
@@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import timber.log.Timber
+@Suppress("LargeClass")
internal class DefaultLegacyWalletConnectRepository(
private val application: Application,
private val wcRequestDeserializer: WcJrpcRequestsDeserializer,
@@ -72,19 +73,23 @@ internal class DefaultLegacyWalletConnectRepository(
}
}
- WalletKit.initialize(Wallet.Params.Init(core = CoreClient)) { error ->
- Timber.e("Error while initializing Web3Wallet: $error")
- scope.launch {
- _events.emit(
- WalletConnectEvents.SessionApprovalError(
- WalletConnectError.ExternalApprovalError(error.throwable.message),
- ),
- )
- }
- }
-
- val walletDelegate = defineWalletDelegate()
- WalletKit.setWalletDelegate(walletDelegate)
+ WalletKit.initialize(
+ Wallet.Params.Init(core = CoreClient),
+ onSuccess = {
+ val walletDelegate = defineWalletDelegate()
+ WalletKit.setWalletDelegate(walletDelegate)
+ },
+ onError = { error ->
+ Timber.e("Error while initializing Web3Wallet: $error")
+ scope.launch {
+ _events.emit(
+ WalletConnectEvents.SessionApprovalError(
+ WalletConnectError.ExternalApprovalError(error.throwable.message),
+ ),
+ )
+ }
+ },
+ )
}
private fun defineWalletDelegate(): WalletKit.WalletDelegate {
@@ -333,7 +338,9 @@ internal class DefaultLegacyWalletConnectRepository(
optionalNamespace.key to Wallet.Model.Namespace.Session(
accounts = accountsOptional.distinct(),
methods = methods,
- chains = userChains.keys.toList(),
+ chains = userChains.keys
+ .filter { it.startsWith(optionalNamespace.key) }
+ .toList(),
events = optionalNamespace.value.events,
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt
index e53938eaf6..7b646bb85f 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt
@@ -155,8 +155,8 @@ class WalletConnectInteractor(
return@onEach
}
val networksFormatted = (wcEvent.requiredChainIds + wcEvent.optionalChainIds)
- .distinct()
.mapNotNull { blockchainHelper.chainIdToFullNameOrNull(it) }
+ .distinct()
.toString()
handler.onProposalReceived(proposal = wcEvent, networksFormatted = networksFormatted)
}
diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt
index 8ede05a98a..bbf156324b 100644
--- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt
@@ -66,7 +66,10 @@ internal class CardSettingsViewModel @Inject constructor(
)
private fun scanCard() = viewModelScope.launch {
- scanCardProcessor.scan(allowsRequestAccessCodeFromRepository = true)
+ scanCardProcessor.scan(
+ analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Settings,
+ allowsRequestAccessCodeFromRepository = true,
+ )
.doOnSuccess { scanResponse ->
val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
if (userWalletId == scannedUserWalletId || scannedUserWalletId == null) {
diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt
index e21d25ea54..e6b36001d8 100644
--- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt
+++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt
@@ -8,7 +8,7 @@ import com.tangem.tap.features.intentHandler.IntentHandler
internal class OnPushClickedIntentHandler(val analyticsEventHandler: AnalyticsEventHandler) : IntentHandler {
override fun handleIntent(intent: Intent?, isFromForeground: Boolean): Boolean {
- val fromPush = intent?.extras?.getBoolean(IS_OPENED_FROM_PUSH) ?: false
+ val fromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) ?: false
return if (fromPush) {
analyticsEventHandler.send(Push.PushNotificationOpened)
@@ -19,6 +19,6 @@ internal class OnPushClickedIntentHandler(val analyticsEventHandler: AnalyticsEv
}
companion object {
- const val IS_OPENED_FROM_PUSH = "IS_OPENED_FROM_PUSH"
+ const val OPENED_FROM_GCM_PUSH = "google.sent_time" // every bundle from FCM contains this key
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt
index 5551a4af79..a1605112fc 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt
@@ -13,7 +13,6 @@ import com.tangem.domain.common.util.twinsIsTwinned
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ProductType
import com.tangem.domain.models.scan.ScanResponse
-import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.visa.model.VisaCardActivationStatus
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
@@ -22,7 +21,6 @@ import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
@@ -109,7 +107,7 @@ object OnboardingHelper {
}
}
- fun saveWallet(
+ suspend fun saveWallet(
alreadyCreatedWallet: UserWallet?,
scanResponse: ScanResponse,
accessCode: String? = null,
@@ -117,46 +115,42 @@ object OnboardingHelper {
hasBackupError: Boolean = false,
) {
Analytics.setContext(scanResponse)
- scope.launch {
- val settingsRepository = store.inject(DaggerGraphState::settingsRepository)
- when {
- // When should save user wallets, then save card without navigate to save wallet screen
- store.inject(DaggerGraphState::walletsRepository).shouldSaveUserWalletsSync() -> {
- store.dispatchWithMain(
- SaveWalletAction.ProvideBackupInfo(
- scanResponse = scanResponse,
- accessCode = accessCode,
- backupCardsIds = backupCardsIds?.toSet(),
- ),
- )
+ val settingsRepository = store.inject(DaggerGraphState::settingsRepository)
+ when {
+ // When should save user wallets, then save card without navigate to save wallet screen
+ store.inject(DaggerGraphState::walletsRepository).shouldSaveUserWalletsSync() -> {
+ store.dispatchWithMain(
+ SaveWalletAction.ProvideBackupInfo(
+ scanResponse = scanResponse,
+ accessCode = accessCode,
+ backupCardsIds = backupCardsIds?.toSet(),
+ ),
+ )
- store.dispatchWithMain(
- SaveWalletAction.SaveWalletAfterBackup(
- hasBackupError = hasBackupError,
- shouldNavigateToWallet = false,
- ),
- )
- }
- // When should not save user wallets but device has biometry and save wallet screen has not been shown,
- // then open save wallet screen
- tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen() -> {
- proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError, alreadyCreatedWallet)
-
- delay(timeMillis = 1_200)
-
- store.dispatchOnMain(
- SaveWalletAction.ProvideBackupInfo(
- scanResponse = scanResponse,
- accessCode = accessCode,
- backupCardsIds = backupCardsIds?.toSet(),
- ),
- )
- }
- // If device has no biometry and save wallet screen has been shown, then go through old scenario
- else -> {
- proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError, alreadyCreatedWallet)
- }
+ store.dispatchWithMain(
+ SaveWalletAction.SaveWalletAfterBackup(
+ hasBackupError = hasBackupError,
+ shouldNavigateToWallet = false,
+ ),
+ )
}
+ // When should not save user wallets but device has biometry and save wallet screen has not been shown,
+ // then open save wallet screen
+ tangemSdkManager.checkCanUseBiometry() && settingsRepository.shouldShowSaveUserWalletScreen() -> {
+ proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError, alreadyCreatedWallet)
+
+ delay(timeMillis = 1_200)
+
+ store.dispatchWithMain(
+ SaveWalletAction.ProvideBackupInfo(
+ scanResponse = scanResponse,
+ accessCode = accessCode,
+ backupCardsIds = backupCardsIds?.toSet(),
+ ),
+ )
+ }
+ // If device has no biometry and save wallet screen has been shown, then go through old scenario
+ else -> proceedWithScanResponse(scanResponse, backupCardsIds, hasBackupError, alreadyCreatedWallet)
}
}
@@ -243,18 +237,7 @@ object OnboardingHelper {
val currencyType = AnalyticsParam.CurrencyType.Blockchain(blockchain)
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
- scope.launch {
- val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
- val isRussia = getUserCountryCodeUseCase.invokeSync().isRight { it is UserCountry.Russia }
-
- val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
- if (isRussia && !onrampFeatureToggles.isFeatureEnabled) {
- val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
- store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData))
- } else {
- store.dispatchOpenUrl(topUpUrl)
- }
- }
+ store.dispatchOpenUrl(topUpUrl)
}
private suspend fun proceedWithScanResponse(
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt
index 37ecb6d41f..4b8ab7545a 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt
@@ -1,17 +1,26 @@
package com.tangem.tap.features.onboarding.products.twins.redux
-import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemError
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.utils.popTo
import com.tangem.core.analytics.Analytics
+import com.tangem.core.analytics.models.AnalyticsParam
+import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
+import com.tangem.core.ui.R
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.toWrappedList
+import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.domain.common.extensions.makePrimaryWalletManager
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.twinsIsTwinned
+import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.legacy.asLockable
+import com.tangem.sdk.extensions.localizedDescriptionRes
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.entities.ProgressState
import com.tangem.tap.common.extensions.*
@@ -185,8 +194,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateSecondWallet))
}
}
- is CompletionResult.Failure -> {
- }
+ is CompletionResult.Failure -> showCardVerificationFailedDialog(result.error)
}
}
}
@@ -208,8 +216,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.CreateThirdWallet))
}
}
- is CompletionResult.Failure -> {
- }
+ is CompletionResult.Failure -> showCardVerificationFailedDialog(result.error)
}
}
}
@@ -218,7 +225,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
scope.launch {
when (val result = manager.complete(action.message)) {
- is Result.Success -> {
+ is CompletionResult.Success -> {
Analytics.send(Onboarding.Twins.SetupFinished())
updateScanResponse(result.data)
@@ -234,8 +241,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
}
}
- is Result.Failure -> {
- }
+ is CompletionResult.Failure -> showCardVerificationFailedDialog(result.error)
}
}
}
@@ -349,6 +355,31 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
}
+private fun showCardVerificationFailedDialog(error: TangemError) {
+ if (error is TangemSdkError.CardVerificationFailed) {
+ Analytics.send(
+ event = OnboardingAnalyticsEvent.Onboarding.OfflineAttestationFailed(AnalyticsParam.ScreensSources.Backup),
+ )
+
+ val resource = error.localizedDescriptionRes()
+ val resId = resource.resId ?: R.string.common_unknown_error
+ val resArgs = resource.args.map { it.value }
+
+ store.inject(DaggerGraphState::uiMessageSender).send(
+ message = Dialogs.cardVerificationFailed(
+ errorDescription = resourceReference(id = resId, resArgs.toWrappedList()),
+ onRequestSupport = {
+ mainScope.launch {
+ store.inject(DaggerGraphState::sendFeedbackEmailUseCase).invoke(
+ type = FeedbackEmailType.CardAttestationFailed,
+ )
+ }
+ },
+ ),
+ )
+ }
+}
+
private fun getPopBackScreen(): KClass {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
index d99a38a119..df9ee2e6b7 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
@@ -4,7 +4,6 @@ import android.net.Uri
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
-import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ifNotNull
import com.tangem.common.extensions.toHexString
@@ -12,9 +11,15 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
+import com.tangem.core.analytics.models.AnalyticsParam.ScreensSources
+import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.toWrappedList
+import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.domain.common.extensions.withIOContext
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.common.util.cardTypesResolver
+import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_IDS
import com.tangem.domain.models.scan.CardDTO.Companion.RING_BATCH_PREFIX
@@ -30,6 +35,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.BackupValidator
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.backup.BackupService
import com.tangem.sdk.api.CreateProductWalletTaskResponse
+import com.tangem.sdk.extensions.localizedDescriptionRes
import com.tangem.tap.*
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
@@ -37,7 +43,6 @@ import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.demo.DemoHelper
-import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
@@ -46,7 +51,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
-import timber.log.Timber
object OnboardingWalletMiddleware {
val handler = onboardingWalletMiddleware
@@ -197,7 +201,7 @@ private fun handleWalletAction(action: Action) {
}
}
-private fun handleFinishBackup(scanResponse: ScanResponse, userWallet: UserWallet? = null) {
+private suspend fun handleFinishBackup(scanResponse: ScanResponse, userWallet: UserWallet? = null) {
val backupState = store.state.onboardingWalletState.backupState
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
@@ -228,31 +232,14 @@ private fun navigateToWalletScreen() {
}
}
-private suspend fun readCard(onSuccess: suspend (ScanResponse) -> Unit, onFailure: (TangemError) -> Unit) {
+private suspend fun readCard(): CompletionResult {
val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = shouldSaveAccessCodes,
)
- store.inject(DaggerGraphState::scanCardProcessor).scan(
- analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Intro,
- onProgressStateChange = { showProgress ->
- if (showProgress) {
- store.dispatch(HomeAction.ScanInProgress(scanInProgress = true))
- } else {
- delay(HIDE_PROGRESS_DELAY)
- store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
- }
- },
- onFailure = {
- Timber.e(it, "Unable to scan card")
- delay(HIDE_PROGRESS_DELAY)
- store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
- onFailure(it)
- },
- onSuccess = onSuccess,
- )
+ return store.inject(DaggerGraphState::scanCardProcessor).scan(analyticsSource = ScreensSources.Intro)
}
private suspend fun loadArtworkForCard(cardId: String, cardPublicKey: ByteArray, defaultArtwork: Uri?): Uri {
@@ -469,6 +456,29 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
val crashlytics = FirebaseCrashlytics.getInstance()
when (val error = result.error) {
+ is TangemSdkError.CardVerificationFailed -> {
+ Analytics.send(
+ event = OnboardingAnalyticsEvent.Onboarding.OfflineAttestationFailed(
+ ScreensSources.Backup,
+ ),
+ )
+
+ val resource = error.localizedDescriptionRes()
+ val resId = resource.resId ?: com.tangem.core.ui.R.string.common_unknown_error
+ val resArgs = resource.args.map { it.value }
+
+ store.inject(DaggerGraphState::uiMessageSender).send(
+ message = Dialogs.cardVerificationFailed(
+ errorDescription = resourceReference(id = resId, resArgs.toWrappedList()),
+ onRequestSupport = {
+ mainScope.launch {
+ store.inject(DaggerGraphState::sendFeedbackEmailUseCase)
+ .invoke(type = FeedbackEmailType.CardAttestationFailed)
+ }
+ },
+ ),
+ )
+ }
is TangemSdkError.BackupFailedNotEmptyWallets -> {
store.dispatchOnMain(
GlobalAction.ShowDialog(
@@ -631,23 +641,22 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
Analytics.send(Onboarding.Finished())
scope.launch {
- store.state.globalState.onboardingState.onboardingManager?.finishActivation(
- cardIds = gatherCardIds(backupState, card),
- )
- }
- if (scanResponse == null) {
- scope.launch {
- readCard(
- onSuccess = { newScanResponse ->
- handleFinishBackup(newScanResponse)
- },
- onFailure = {
- store.dispatchNavigationAction(AppRouter::pop)
- },
+ launch {
+ store.state.globalState.onboardingState.onboardingManager?.finishActivation(
+ cardIds = gatherCardIds(backupState, card),
)
}
- } else {
- handleFinishBackup(scanResponse)
+
+ with(scanResponse) {
+ if (this == null) {
+ when (val result = readCard()) {
+ is CompletionResult.Success -> handleFinishBackup(result.data)
+ is CompletionResult.Failure -> store.dispatchNavigationAction(AppRouter::pop)
+ }
+ } else {
+ handleFinishBackup(scanResponse = this)
+ }
+ }
}
}
is BackupAction.FinishBackup -> {
@@ -663,40 +672,39 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
scanResponse = requireNotNull(value = scanResponse, lazyMessage = { "ScanResponse is null" }),
backupState = backupState,
)
-
- val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
- when (backupState.startedSource) {
- BackupStartedSource.Onboarding -> saveWallet(
- userWalletsListManager = userWalletsListManager,
- userWallet = userWallet,
- scanResponse = scanResponse,
- backupState = backupState,
- )
- BackupStartedSource.CreateBackup -> updateWallet(
- userWalletsListManager = userWalletsListManager,
- userWallet = userWallet,
- backupState = backupState,
- )
- }
} else {
delay(HIDE_PROGRESS_DELAY)
- readCard(
- onSuccess = { newScanResponse ->
- scanResponse = newScanResponse
- userWallet = createUserWallet(newScanResponse, backupState)
- },
- onFailure = {
+ when (val result = readCard()) {
+ is CompletionResult.Failure -> {
store.dispatchNavigationAction(AppRouter::pop)
- },
+ return@launch
+ }
+ is CompletionResult.Success -> {
+ scanResponse = result.data
+ userWallet = createUserWallet(scanResponse = result.data, backupState = backupState)
+ }
+ }
+ }
+
+ val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
+ when (backupState.startedSource) {
+ BackupStartedSource.Onboarding -> saveWallet(
+ userWalletsListManager = userWalletsListManager,
+ userWallet = userWallet,
+ scanResponse = scanResponse,
+ backupState = backupState,
+ )
+ BackupStartedSource.CreateBackup -> updateWallet(
+ userWalletsListManager = userWalletsListManager,
+ userWallet = userWallet,
+ backupState = backupState,
)
}
scope.launch {
- userWallet?.let {
- if (it.scanResponse.cardTypesResolver.isWallet2() && it.isImported) {
- store.inject(DaggerGraphState::walletsRepository).markWallet2WasCreated(it.walletId)
- }
+ if (userWallet.scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported) {
+ store.inject(DaggerGraphState::walletsRepository).markWallet2WasCreated(userWallet.walletId)
}
}
@@ -711,7 +719,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
// All cardIds may already be activated if the backup was skipped before.
if (notActivatedCardIds.isEmpty()) {
delay(1000)
- store.dispatchWithMain(BackupAction.BackupFinished(userWallet?.walletId))
+ store.dispatchWithMain(BackupAction.BackupFinished(userWallet.walletId))
return@launch
}
@@ -719,8 +727,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
store.state.globalState.onboardingState.onboardingManager?.finishActivation(notActivatedCardIds)
handleFinishBackup(requireNotNull(scanResponse), userWallet)
- delay(1000)
- store.dispatchWithMain(BackupAction.BackupFinished(userWalletId = userWallet?.walletId))
+ store.dispatchWithMain(BackupAction.BackupFinished(userWalletId = userWallet.walletId))
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt
index 6c9ed1d2e8..9bda54aa7d 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt
@@ -6,15 +6,16 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.Analytics
import com.tangem.domain.onramp.model.OnrampSource
-import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
-import com.tangem.tap.common.extensions.*
-import com.tangem.tap.common.redux.AppDialog
+import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
+import com.tangem.tap.common.extensions.dispatchNavigationAction
+import com.tangem.tap.common.extensions.dispatchOpenUrl
+import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
@@ -89,19 +90,6 @@ object TradeCryptoMiddleware {
)
scope.launch {
- val getUserCountryCodeUseCase = store.inject(DaggerGraphState::getUserCountryUseCase)
- val onrampFeatureToggles = store.inject(DaggerGraphState::onrampFeatureToggles)
-
- val isRussia = getUserCountryCodeUseCase.invokeSync().isRight { it is UserCountry.Russia }
-
- if (action.checkUserLocation && isRussia && !onrampFeatureToggles.isFeatureEnabled) {
- val dialogData = topUrl?.let {
- AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl = it)
- }
- store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(data = dialogData))
- return@launch
- }
-
if (currency is CryptoCurrency.Token && currency.network.isTestnet) {
val walletManager = store.inject(DaggerGraphState::walletManagersFacade)
.getOrCreateWalletManager(
diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt
index 6de664b3ce..bd75d53d4e 100644
--- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt
+++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt
@@ -4,6 +4,7 @@ import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.signer.TransactionSignerFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter
+import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
@@ -79,4 +80,5 @@ data class DaggerGraphState(
val appPreferencesStore: AppPreferencesStore? = null,
val clipboardManager: ClipboardManager? = null,
val settingsManager: SettingsManager? = null,
+ val uiMessageSender: UiMessageSender? = null,
) : StateType
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_russians_cardholders_warning.xml b/app/src/main/res/layout/dialog_russians_cardholders_warning.xml
deleted file mode 100644
index a666076b95..0000000000
--- a/app/src/main/res/layout/dialog_russians_cardholders_warning.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt
index 2f2e54876b..dceee7e356 100644
--- a/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt
+++ b/app/src/test/kotlin/com/tangem/tap/domain/card/CryptoCurrenciesMocks.kt
@@ -48,7 +48,7 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
val network = Network(
id = Network.ID(blockchain.id),
backendId = blockchain.toNetworkId(),
- name = blockchain.getNetworkName(),
+ name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath(
blockchain,
@@ -76,7 +76,7 @@ internal class CryptoCurrenciesMocks(private val scanResponse: ScanResponse) {
network = Network(
id = Network.ID(value = blockchain.id),
backendId = "NEVER-MIND",
- name = blockchain.getNetworkName(),
+ name = blockchain.fullName,
currencySymbol = "NEVER-MIND",
derivationPath = Network.DerivationPath.Custom(
value = derivationBlockchain.derivationPath(
diff --git a/build.gradle.kts b/build.gradle.kts
index 188c0a6ef6..0ace95bd3b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -10,6 +10,7 @@ plugins {
alias(deps.plugins.hilt.android) apply false
alias(deps.plugins.google.services) apply false
alias(deps.plugins.firebase.crashlytics) apply false
+ alias(deps.plugins.firebase.perf) apply false
alias(deps.plugins.room) apply false
}
diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
index 2f18a10314..d25fea1cbc 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt
@@ -173,7 +173,7 @@ object NotificationsFactory {
val spendingAmount = if (cryptoCurrency is CryptoCurrency.Token) {
feeAmount
} else {
- sendingAmount
+ sendingAmount + feeAmount
}
val diff = balance.minus(spendingAmount)
if (existentialDeposit != null && diff >= BigDecimal.ZERO && existentialDeposit > diff) {
diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
index 723451acf5..6e4b6e4a13 100644
--- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
+++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt
@@ -79,6 +79,8 @@ sealed class AnalyticsParam {
data object Buy : ScreensSources("Buy")
data object Swap : ScreensSources("Swap")
data object Sell : ScreensSources("Sell")
+ data object Backup : ScreensSources("Backup")
+ data object Onboarding : ScreensSources("Onboarding")
}
sealed class TxSentFrom(val value: String) {
diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt
new file mode 100644
index 0000000000..f60f3b53fe
--- /dev/null
+++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt
@@ -0,0 +1,24 @@
+package com.tangem.core.analytics.models.event
+
+import com.tangem.core.analytics.models.AnalyticsEvent
+import com.tangem.core.analytics.models.AnalyticsParam
+
+sealed class OnboardingAnalyticsEvent(
+ category: String,
+ event: String,
+ params: Map = mapOf(),
+) : AnalyticsEvent(category, event, params) {
+
+ sealed class Onboarding(
+ event: String,
+ params: Map = mapOf(),
+ ) : OnboardingAnalyticsEvent(category = "Error", event = event, params = params) {
+
+ data class OfflineAttestationFailed(
+ val source: AnalyticsParam.ScreensSources,
+ ) : Onboarding(
+ event = "Offline Attestation Failed",
+ params = mapOf(AnalyticsParam.SOURCE to source.value),
+ )
+ }
+}
\ No newline at end of file
diff --git a/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json b/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
index ad3ae9c4f6..64770f0109 100644
--- a/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
+++ b/core/config-toggles/src/main/assets/configs/excluded_blockchains_config.json
@@ -11,22 +11,10 @@
"name": "NEXA/test",
"version": "undefined"
},
- {
- "name": "fact0rn",
- "version": "undefined"
- },
{
"name": "vanar-chain",
"version": "undefined"
},
- {
- "name": "bitrock",
- "version": "undefined"
- },
- {
- "name": "odyssey",
- "version": "undefined"
- },
{
"name": "sonic",
"version": "undefined"
diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
index 353f66d62e..29dbfa7630 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
@@ -40,6 +40,7 @@ internal object NetworkModule {
private const val PROD_V2_TANGEM_TECH_BASE_URL = "https://api.tangem-tech.com/v2/"
private const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L
+ private const val STAKE_KIT_API_TIMEOUT_SECONDS = 60L
@Provides
@Singleton
@@ -89,6 +90,12 @@ internal object NetworkModule {
moshi = moshi,
context = context,
apiConfigsManager = apiConfigsManager,
+ timeouts = Timeouts(
+ callTimeoutSeconds = STAKE_KIT_API_TIMEOUT_SECONDS,
+ connectTimeoutSeconds = STAKE_KIT_API_TIMEOUT_SECONDS,
+ readTimeoutSeconds = STAKE_KIT_API_TIMEOUT_SECONDS,
+ writeTimeoutSeconds = STAKE_KIT_API_TIMEOUT_SECONDS,
+ ),
clientBuilder = {
addInterceptor(
NetworkLogsSaveInterceptor(appLogsStore),
@@ -262,6 +269,7 @@ internal object NetworkModule {
moshi: Moshi,
context: Context,
apiConfigsManager: ApiConfigsManager,
+ timeouts: Timeouts = Timeouts(),
clientBuilder: OkHttpClient.Builder.() -> OkHttpClient.Builder = { this },
): T {
val environmentConfig = apiConfigsManager.getEnvironmentConfig(id)
@@ -273,6 +281,23 @@ internal object NetworkModule {
.client(
OkHttpClient.Builder()
.applyApiConfig(id, apiConfigsManager)
+ .applyTimeoutAnnotations()
+ .let { builder ->
+ var b = builder
+ if (timeouts.callTimeoutSeconds != null) {
+ b = b.callTimeout(timeouts.callTimeoutSeconds, TimeUnit.SECONDS)
+ }
+ if (timeouts.connectTimeoutSeconds != null) {
+ b = b.connectTimeout(timeouts.connectTimeoutSeconds, TimeUnit.SECONDS)
+ }
+ if (timeouts.readTimeoutSeconds != null) {
+ b = b.readTimeout(timeouts.readTimeoutSeconds, TimeUnit.SECONDS)
+ }
+ if (timeouts.writeTimeoutSeconds != null) {
+ b = b.writeTimeout(timeouts.writeTimeoutSeconds, TimeUnit.SECONDS)
+ }
+ b
+ }
.addLoggers(context)
.clientBuilder()
.build(),
diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt
index 428169c98e..b28130b286 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeStateStore.kt
@@ -2,6 +2,7 @@ package com.tangem.datasource.local.datastore
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
/**
* Runtime store
@@ -16,6 +17,8 @@ interface RuntimeStateStore {
/** Store [value] */
suspend fun store(value: T)
+ suspend fun update(function: (T) -> T)
+
companion object {
/**
@@ -32,6 +35,10 @@ interface RuntimeStateStore {
override suspend fun store(value: T) {
flow.value = value
}
+
+ override suspend fun update(function: (T) -> T) {
+ flow.update(function)
+ }
}
}
}
\ No newline at end of file
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 9d34a45453..ae312288dd 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -658,9 +658,7 @@
Durch das Zurücksetzen auf Werkseinstellungen wird die Wallet vollständig von der ausgewählten Karte oder Ring gelöscht. Du kannst die aktuelle Wallet nicht wiederherstellen oder die Karte oder Ring verwenden, um den Zugangscode wiederherzustellen.
Beim Zurücksetzen auf die Werkseinstellungen wird die Wallet der ausgewählten Karte oder Ring vollständig gelöscht und aus der App entfernt. Es ist nicht möglich, die aktuelle Wallet wiederherzustellen.
Ringbesitzer erhalten bis zum 15.11. 3 provisionsfreie Swaps auf Changelly!
- Jetzt mit 0 %% Gebühren tauschen!
- Besitzt du eine Bankkarte aus einem anderen Land und eine Aufenthaltserlaubnis oder Registrierung außerhalb der Russischen Föderation?
- Russische Bankkarten werden derzeit nicht akzeptiert
+ Jetzt mit 0 % Gebühren tauschen!
Melde dich bei der App an und überprüfe dein Guthaben, ohne die Karte oder Ring zu scannen
Zugriff auf die App
Nutzung biometrischer Daten zulassen
@@ -1000,7 +998,7 @@
%s Netzwerk
Die Adresse wurde erfolgreich kopiert
Keine Internetverbindung
- Jetzt mit 10 %% Rabatt kaufen
+ Jetzt mit 10 % Rabatt kaufen
Greife auf 13.000+ Kryptowährungen zu. Kaufe, verkaufe, tausche und stake Sie mit einem einzigen Fingertipp.\nVerbinde bis zu drei Karten für ein Backup.
Entdecke die Tangem Wallet
Wallet-Einstellungen
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index 1c536e5ea2..5bd7fecb44 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -654,9 +654,7 @@
El restablecimiento de fábrica eliminará completamente la billetera de la tarjeta seleccionada. No podrá restaurar la billetera actual ni usar la tarjeta para recuperar el código de acceso.
El reseteo a valores de fábrica eliminará completamente la billetera de la tarjeta/anillo seleccionado y lo eliminará de la app. No podrá restaurar la billetera actual.
Si tienes un Anillo Tangem, ¡3 swaps sin comisión en Changelly hasta el 15/11!
- ¡Intercambia con 0%% de comisión!
- ¿Tiene una tarjeta bancaria de otro país y un permiso de residencia o registro fuera de la Federación Rusa?
- Las tarjetas bancarias rusas no se aceptan actualmente
+ ¡Intercambia con 0% de comisión!
Inicie sesión en la app y comprueba su saldo sin escanear la tarjeta o el anillo
Acceder a la app
Permitir el uso de biometría
@@ -996,7 +994,7 @@
%s red
La dirección se copió correctamente
Sin conexión a internet
- Consíguelo ahora con un 10 %% de descuento
+ Consíguelo ahora con un 10 % de descuento
Accede a más de 13 000 criptomonedas. Compra, vende, intercambia y realiza staking con un solo toque.\nVincula hasta tres tarjetas para hacer copias de seguridad.
Descubre Tangem Wallet
Ajustes de la wallet
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 3359111424..3496503f2a 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -654,9 +654,7 @@
La réinitialisation aux paramètres d\'usine supprimera complètement le portefeuille de la carte sélectionnée. Vous ne pourrez pas restaurer le portefeuille actuel ni utiliser la carte pour récupérer le code d\'accès.
La réinitialisation aux paramètres d\'usine supprimera complètement le portefeuille de la carte sélectionnée et le supprimera de l\'application. Vous ne pourrez pas restaurer le portefeuille actuel.
Les propriétaires du Tangem Ring ont droit à 3 swap gratis sur Changelly jusqu\'au 15/11 !
- Échangez avec 0 %% de frais !
- Avez-vous une carte bancaire d\'un autre pays et un permis de séjour ou une inscription en dehors de la Fédération de Russie ?
- Les cartes bancaires russes ne sont actuellement pas acceptées
+ Échangez avec 0 % de frais !
Connectez-vous à l\'application et vérifiez votre solde sans scanner la carte
Accéder à l\'application
Autoriser l\'utilisation de la biométrie
@@ -996,7 +994,7 @@
%s réseau
L\'adresse a été copiée avec succès
Pas de connexion internet
- Obtenez-le maintenant avec 10 %% de réduction
+ Obtenez-le maintenant avec 10 % de réduction
Accédez à plus de 13 000 cryptomonnaies. Achetez, vendez, échangez et stakez en un seul clic.\nAssociez jusqu\'à trois cartes pour une sauvegarde.
Découvrez le Portefeuille Tangem
Paramètres du portefeuille
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index 7d4ad5df3b..287efe4ff4 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -648,9 +648,7 @@
工場出荷時設定にリセットすると、選択したカードやリングからウォレットが完全に削除されます。現在のウォレットを復元したり、カードやリングを使用してアクセスコードを復元することはできません。
工場出荷時の状態にリセットすると、選択したカードやリングからウォレットが完全に削除され、アプリから削除されます。現在のウォレットを復元することはできません。
Tangem Ringユーザーは、11/15日までChangelly経由でスワップを3回手数料ゼロで行えます!
- 今すぐ手数料0%%でスワップしましょう!
- 他の国の銀行カードと、ロシア連邦外での居住許可証または登録をお持ちですか?
- ロシアの銀行カードは現在ご利用いただけません
+ 今すぐ手数料0%でスワップしましょう!
アプリにログインして、カードまたはリングをスキャンせずに残高を確認できます
アプリにアクセスする
生体認証の使用を許可する
@@ -990,7 +988,7 @@
%sネットワーク
アドレスがクリップボードにコピーされました
インターネット接続がありません
- 今すぐ10 %%オフで購入
+ 今すぐ10 %オフで購入
1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。
Tangemウォレットを見る
ウォレット設定
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 55539a0c30..373ef88731 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -672,9 +672,7 @@
Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты или кольца. Вы не сможете восстановить текущий кошелек или использовать данную карту или кольцо для восстановления кода доступа.
Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты или кольца. Вы не сможете восстановить текущий кошелек.
Владельцам колец — 3 обмена без комиссии на Changelly до 15.11!
- Обмен с 0%% комиссией!
- У вас есть карта банка другой страны, а также вид на жительство или регистрация вне РФ?
- Карты банков РФ в данный момент не принимаются
+ Обмен с 0% комиссией!
Войдите в приложение и следите за своим балансом без сканирования карты или кольца
Доступ в приложение
Использовать биометрию
@@ -1013,7 +1011,7 @@
Сеть %s
Адрес скопирован в буфер обмена
Нет соединения с интернетом
- Получить с 10%% скидкой
+ Получить с 10% скидкой
Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменивайте и стейкайте в один клик. Свяжите до трех карт для резервного копирования.
Откройте Tangem Wallet
Настройки кошелька
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index 4671e06916..70bf6356ac 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -51,6 +51,8 @@
Більше не показувати
Зрозуміло
Баланси приховані
+ Згідно інформації від розробників мережі, токени Kaspa знаходяться у режимі бета. Слідкуйте за оновленнями!
+ Бета режим
Будь ласка, відскануйте картку або кільце
Будь ласка, спробуйте знову через 30 секунд або відскануйте картку або кільце
Забагато спроб
@@ -672,9 +674,7 @@
Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки або кільця. Ви не зможете відновити поточний гаманець або використати цю картку або кільце для відновлення коду доступу.
Скидання до заводських налаштувань призведе до повного видалення гаманця з обраної картки або кільця. Ви не зможете відновити поточний гаманець.
Власникам кілець - 3 обміни без комісії на Changelly до 15.11!
- Обмін з 0%% комісією!
- У вас є банківська картка іншої країни та посвідка на проживання або реєстрація за межами Російської Федерації?
- Російські банківські картки наразі не приймаються
+ Обмін з 0% комісією!
Увійдіть у додаток та слідкуйте за своїм балансом без сканування картки чи кільця
Доступ до додатку
Використовувати біометрію
@@ -1014,7 +1014,7 @@
Мережа %s
Адреса скопійована в буфер обміну
Немає підключення до інтернету
- Отримати з 10%% знижкою
+ Отримати з 10% знижкою
Доступ до 13,000+ криптовалют. Купуйте, продавайте, обмінюйте та стейкайте одним дотиком.\nЗ’єднайте до трьох карток для бекапу.
Відкрийте Tangem Wallet
Налаштування гаманця
diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml
index f144efa47f..1e2134aa6e 100644
--- a/core/res/src/main/res/values-zh-rTW/strings.xml
+++ b/core/res/src/main/res/values-zh-rTW/strings.xml
@@ -237,7 +237,6 @@
我了解執行此操作後,我將無法再訪問當前錢包
恢復原廠設置將從所選卡中完全刪除錢包。您將無法恢復當前錢包或使用卡恢復訪問密碼
恢復原廠設置將從所選卡中完全刪除錢包並將其從應用程序中刪除。您將無法恢復當前錢包
- 目前不接受俄羅斯銀行卡
登錄應用程序並在不掃描卡片的情況下檢查您的資產
訪問應用程序
允許使用生物辨識
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 6f0a7f4223..868f58f4a9 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -658,9 +658,7 @@
Factory Reset will completely delete the wallet from the selected card or ring. You will not be able to restore the current wallet or use the card or ring to recover the access code.
Factory Reset will completely delete the wallet from the selected card or ring and remove it from the app. You will not be able to restore the current wallet.
Ring owners get 3 commission-free swaps on Changelly until 15.11!
- Swap With 0%% Fees Now!
- Do you have a bank card from another country and a residence permit or registration outside the Russian Federation?
- Russian bank cards are not currently accepted
+ Swap With 0% Fees Now!
Log into the app and check your balance without scanning the card or ring
Access the app
Allow to use biometrics
@@ -1000,7 +998,7 @@
%s network
Address was copied to clipboard
No internet connection
- Get now with 10%% off
+ Get now with 10% off
Access 13,000+ cryptocurrencies. Buy, sell, swap, and stake with a single tap.\nLink up to three cards for a backup.
Discover Tangem Wallet
Wallet settings
@@ -1084,7 +1082,7 @@
Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free.
Swipe down to refresh or try again later.
Some networks are unreachable
- Some token balances could not updated
+ Some token balances could not be updated
This is a Testnet card. It cannot process transactions and should only be used for testing and development purposes.
For testing purposes only
Discard
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt
index 220b5c82ca..7aeca782bf 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt
@@ -1,6 +1,5 @@
package com.tangem.core.ui.components.transactions
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -119,13 +118,12 @@ private fun PendingTxsBlock(pendingTxs: ImmutableList, isBalan
}
}
-@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) {
item(key = state::class.java, contentType = state::class.java) {
EmptyTransactionBlock(
state = state,
modifier = modifier
- .animateItemPlacement()
+ .animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12)
.fillMaxWidth(),
)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/message/dialog/Dialogs.kt b/core/ui/src/main/java/com/tangem/core/ui/message/dialog/Dialogs.kt
new file mode 100644
index 0000000000..86aa88795f
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/message/dialog/Dialogs.kt
@@ -0,0 +1,39 @@
+package com.tangem.core.ui.message.dialog
+
+import com.tangem.core.ui.R
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.message.DialogMessage
+import com.tangem.core.ui.message.EventMessageAction
+
+/**
+[REDACTED_AUTHOR]
+ */
+object Dialogs {
+
+ /**
+ * Card verification failed dialog
+ *
+ * @param errorDescription sdk error description
+ * @param onRequestSupport lambda be invoked when request support action is clicked
+ * @param onCancelClick lambda be invoked when cancel action is clicked
+ */
+ fun cardVerificationFailed(
+ errorDescription: TextReference,
+ onRequestSupport: () -> Unit,
+ onCancelClick: () -> Unit = {},
+ ): DialogMessage {
+ return DialogMessage(
+ message = errorDescription,
+ title = resourceReference(id = R.string.security_alert_title),
+ isDismissable = false,
+ firstActionBuilder = {
+ EventMessageAction(
+ title = resourceReference(id = R.string.alert_button_request_support),
+ onClick = onRequestSupport,
+ )
+ },
+ secondActionBuilder = { cancelAction(onClick = onCancelClick) },
+ )
+ }
+}
\ No newline at end of file
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
index fb8608e78b..7b9c905f0d 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt
@@ -89,7 +89,7 @@ class CryptoCurrencyFactory(
return CryptoCurrency.Coin(
id = getCoinId(network, blockchain.toCoinId()),
network = network,
- name = blockchain.fullName,
+ name = blockchain.getCoinName(),
symbol = blockchain.currency,
iconUrl = getCoinIconUrl(blockchain),
decimals = blockchain.decimals(),
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt
index 09a0186bf9..ccf75203ca 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/NetworkOperations.kt
@@ -30,7 +30,7 @@ fun getNetwork(
return Network(
id = Network.ID(blockchain.id),
backendId = blockchain.toNetworkId(),
- name = blockchain.getNetworkName(),
+ name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = getNetworkDerivationPath(
blockchain = blockchain,
@@ -59,7 +59,7 @@ fun getNetwork(
return Network(
id = networkId,
backendId = blockchain.toNetworkId(),
- name = blockchain.getNetworkName(),
+ name = blockchain.fullName,
isTestnet = blockchain.isTestnet(),
derivationPath = derivationPath,
currencySymbol = blockchain.currency,
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
index 7cda282ecb..24e9f844e5 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/ResponseCryptoCurrenciesFactory.kt
@@ -2,7 +2,6 @@ package com.tangem.data.common.currency
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
-import com.tangem.blockchainsdk.compatibility.l2BlockchainsList
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toCoinId
@@ -78,7 +77,7 @@ class ResponseCryptoCurrenciesFactory(
return CryptoCurrency.Coin(
id = getCoinId(network, blockchain.toCoinId()),
network = network,
- name = blockchain.getNameForCoin(responseToken),
+ name = blockchain.getCoinName(),
symbol = blockchain.getSymbolForCoin(responseToken),
decimals = responseToken.decimals,
iconUrl = getCoinIconUrl(blockchain),
@@ -86,21 +85,6 @@ class ResponseCryptoCurrenciesFactory(
)
}
- private fun Blockchain.getNameForCoin(responseToken: UserTokensResponse.Token): String {
- return when (this) {
- // workaround: for Blockchains full name different than backend name,
- // get name and symbol from enum Blockchain until backend renamed
- // [REDACTED_JIRA]
- Blockchain.Dischain,
- Blockchain.Telos,
- Blockchain.Cronos,
- Blockchain.TON,
- in l2BlockchainsList,
- -> this.fullName
- else -> responseToken.name
- }
- }
-
private fun Blockchain.getSymbolForCoin(responseToken: UserTokensResponse.Token): String {
return when (this) {
// workaround: Dischain was renamed but backend still returns the old name,
diff --git a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
index 6e2f02f44c..84780326f4 100644
--- a/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
+++ b/data/manage-tokens/src/main/kotlin/com/tangem/data/managetokens/DefaultManageTokensRepository.kt
@@ -295,7 +295,7 @@ internal class DefaultManageTokensRepository(
)
return if (!canHandleBlockchain) {
- CurrencyUnsupportedState.UnsupportedNetwork(networkName = blockchain.getNetworkName())
+ CurrencyUnsupportedState.UnsupportedNetwork(networkName = blockchain.fullName)
} else {
null
}
@@ -314,10 +314,10 @@ internal class DefaultManageTokensRepository(
return when {
// refactor this later by moving all this logic in card config
blockchain == Blockchain.Solana && !supportedTokens.contains(Blockchain.Solana) -> {
- CurrencyUnsupportedState.Token.NetworkTokensUnsupported(networkName = blockchain.getNetworkName())
+ CurrencyUnsupportedState.Token.NetworkTokensUnsupported(networkName = blockchain.fullName)
}
!userWallet.scanResponse.card.canHandleToken(supportedTokens, blockchain, cardTypesResolver) -> {
- CurrencyUnsupportedState.Token.UnsupportedCurve(networkName = blockchain.getNetworkName())
+ CurrencyUnsupportedState.Token.UnsupportedCurve(networkName = blockchain.fullName)
}
else -> null
}
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/paging/CoinsResponseConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/paging/CoinsResponseConverter.kt
index 611ccafe2b..c2b2108053 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/paging/CoinsResponseConverter.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/paging/CoinsResponseConverter.kt
@@ -32,7 +32,7 @@ internal object CoinsResponseConverter : Converter> {
Token.Network(
networkId = network.networkId,
standardType = getNetworkStandardType(blockchain).name,
- name = blockchain.getNetworkName(),
+ name = blockchain.fullName,
address = network.contractAddress,
iconUrl = getIconUrl(network.networkId, value.imageHost),
decimalCount = network.decimalCount?.toInt(),
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
index 4f9c1eecb2..80a7803738 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt
@@ -417,16 +417,16 @@ internal class DefaultCurrenciesRepository(
}
}
- override suspend fun getFeePaidCurrency(userWalletId: UserWalletId, currency: CryptoCurrency): FeePaidCurrency {
+ override suspend fun getFeePaidCurrency(userWalletId: UserWalletId, network: Network): FeePaidCurrency {
return withContext(dispatchers.io) {
- val blockchain = Blockchain.fromId(currency.network.id.value)
+ val blockchain = Blockchain.fromId(network.id.value)
when (val feePaidCurrency = blockchain.feePaidCurrency()) {
FeePaidSdkCurrency.Coin -> FeePaidCurrency.Coin
FeePaidSdkCurrency.SameCurrency -> FeePaidCurrency.SameCurrency
is FeePaidSdkCurrency.Token -> {
val balance = walletManagersFacade.tokenBalance(
userWalletId = userWalletId,
- network = currency.network,
+ network = network,
name = feePaidCurrency.token.name,
symbol = feePaidCurrency.token.symbol,
contractAddress = feePaidCurrency.token.contractAddress,
@@ -434,7 +434,7 @@ internal class DefaultCurrenciesRepository(
id = feePaidCurrency.token.id,
)
FeePaidCurrency.Token(
- tokenId = getTokenId(network = currency.network, sdkToken = feePaidCurrency.token),
+ tokenId = getTokenId(network = network, sdkToken = feePaidCurrency.token),
name = feePaidCurrency.token.name,
symbol = feePaidCurrency.token.symbol,
contractAddress = feePaidCurrency.token.contractAddress,
diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt
index 1b4083ad77..c6e3354caa 100644
--- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt
+++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt
@@ -20,13 +20,14 @@ import com.tangem.utils.coroutines.runCatching
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
internal class DefaultWalletsRepository(
private val appPreferencesStore: AppPreferencesStore,
private val tangemTechApi: TangemTechApi,
private val userWalletsStore: UserWalletsStore,
- private val seedPhraseNotificationVisibilityStore: RuntimeStateStore,
+ private val seedPhraseNotificationVisibilityStore: RuntimeStateStore