diff --git a/app/src/debug/res/values-ru/strings.xml b/app/src/debug/res/values-ru/strings.xml
index 1abc31ee12..682b552975 100644
--- a/app/src/debug/res/values-ru/strings.xml
+++ b/app/src/debug/res/values-ru/strings.xml
@@ -1,6 +1,6 @@
- DebugTangem
+ DebugTangem
\ No newline at end of file
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
index bf9ef4d842..764dd201cc 100644
--- a/app/src/debug/res/values/strings.xml
+++ b/app/src/debug/res/values/strings.xml
@@ -1,6 +1,6 @@
- DebugTangem
+ DebugTangem
\ No newline at end of file
diff --git a/app/src/debug_beta/res/values-ru/strings.xml b/app/src/debug_beta/res/values-ru/strings.xml
index 3e2d147b31..3ae149049f 100644
--- a/app/src/debug_beta/res/values-ru/strings.xml
+++ b/app/src/debug_beta/res/values-ru/strings.xml
@@ -1,6 +1,6 @@
- BetaTangem
+ BetaTangem
\ No newline at end of file
diff --git a/app/src/debug_beta/res/values/strings.xml b/app/src/debug_beta/res/values/strings.xml
index 3e2d147b31..3ae149049f 100644
--- a/app/src/debug_beta/res/values/strings.xml
+++ b/app/src/debug_beta/res/values/strings.xml
@@ -1,6 +1,6 @@
- BetaTangem
+ BetaTangem
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e7d11764b4..f09883d42e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,7 +29,7 @@
{
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context)
is BackupDialog.ConfirmDiscardingBackup -> ConfirmDiscardingBackupDialog.create(context)
is SaltPayDialog.Activation.NoGas -> NoFundsForActivationDialog.create(context)
+ is SaltPayDialog.Activation.PutVisaCard -> PutVisaCardDialog.create(context)
is SaltPayDialog.Activation.OnError -> RegistrationErrorDialog.create(context, state.dialog)
is WalletDialog.CurrencySelectionDialog -> CurrencySelectionDialog.create(state.dialog, context)
is WalletDialog.ChooseTradeActionDialog -> ChooseTradeActionBottomSheetDialog(context, state.dialog)
diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
index f60b23937b..ec30d82d45 100644
--- a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt
@@ -42,7 +42,7 @@ class IntentHandler {
intent.action = null
store.dispatch(NavigationAction.NavigateTo(AppScreen.Home))
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
- store.dispatch(HomeAction.ReadCard)
+ store.dispatch(HomeAction.ReadCard())
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt
index 264c3a5567..49a4b4854f 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt
@@ -2,79 +2,157 @@ package com.tangem.tap.common.analytics.converters
import com.tangem.common.Converter
import com.tangem.common.extensions.isZero
-import com.tangem.domain.common.CardTypesResolver
+import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
+import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.wallet.redux.ProgressState
-import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalCryptoAmount
+import timber.log.Timber
+import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
-class BasicSignInEventConverter(
- private val scanResponse: ScanResponse,
-) : Converter {
+class BasicEventsPreChecker {
- override fun convert(value: WalletState): Basic.SignedIn? {
- if (!statesIsReadyToCreateEvent(scanResponse.cardTypesResolver, value)) return null
- val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) ?: return null
+ fun tryToSend(converterData: BasicEventsSourceData) {
+ if (!isReadyToSend(converterData)) return
+
+ BasicSignInEventConverter().convert(converterData)?.let { Analytics.send(it) }
+ BasicTopUpEventConverter().convert(converterData)?.let { Analytics.send(it) }
+ }
+
+ private fun isReadyToSend(data: BasicEventsSourceData): Boolean {
+ val (scanResponse, walletState, biometricsWalletDataModels) = data
+ if (walletState.derivationsCheckIsScheduled) {
+ Timber.d("FAILED: derivationsCheckIsScheduled")
+ return false
+ }
+ if (scanResponse.cardTypesResolver.isMultiwalletAllowed() && walletState.missingDerivations.isNotEmpty()) {
+ Timber.d("FAILED: isMultiwalletAllowed || missingDerivations.isNotEmpty")
+ return false
+ }
+
+ if (biometricsWalletDataModels == null) {
+ Timber.d("SWITCH: OLD")
+ val walletsDataFromStores = data.walletState.walletsDataFromStores
+ if (walletsDataFromStores.isEmpty()) {
+ Timber.d("FAILED: walletsDataFromStores.isEmpty")
+ return false
+ }
+
+ val totalBalanceState = data.walletState.totalBalance?.state
+ if (totalBalanceState == null || totalBalanceState == ProgressState.Loading ||
+ totalBalanceState == ProgressState.Refreshing
+ ) {
+ Timber.d("FAILED: totalBalanceState: ${totalBalanceState?.name}")
+ return false
+ }
+
+ val balancesCount = walletsDataFromStores
+ .map { if (it.currencyData.amount == null) 0 else 1 }
+ .reduce { acc, i -> acc + i }
+
+ if (balancesCount != walletsDataFromStores.size) {
+ Timber.d("FAILED: balancesCount != walletsDataFromStores.size")
+ return false
+ }
+ } else {
+ Timber.d("SWITCH: BIOMETRICS")
+ if (biometricsWalletDataModels.isEmpty()) {
+ Timber.d("FAILED: biometricsWalletDataModels.isEmpty")
+ return false
+ }
+
+ if (biometricsWalletDataModels.any {
+ it.status is WalletDataModel.Loading ||
+ it.status is WalletDataModel.NoAccount ||
+ it.status is WalletDataModel.Unreachable ||
+ it.status is WalletDataModel.MissedDerivation ||
+ it.status.isErrorStatus
+ }) {
+ Timber.d("FAILED: by status")
+ return false
+ }
+ }
+
+ Timber.d("SUCCESS")
+ return true
+ }
+}
+
+/**
+ * With biometrics enabled, we should check its storage instead of "WalletState.walletsDataFromStores"
+ * because the latter is updated after some time.
+ * @property biometricsWalletDataModels - wallet data models from the 'WalletStoresManager'. If null, then
+ * the "WalletState.walletsDataFromStores" will be used to determine appropriate state
+ */
+data class BasicEventsSourceData(
+ val scanResponse: ScanResponse,
+ val walletState: WalletState,
+ val biometricsWalletDataModels: List?,
+) {
+ val batchId: String by lazy { scanResponse.card.batchId }
+
+ val userWalletIdStringValue: String? by lazy {
+ UserWalletIdBuilder.scanResponse(scanResponse)
+ .build()
+ ?.stringValue
+ }
+
+ val paramCardCurrency: AnalyticsParam.CardCurrency? by lazy {
+ ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver)
+ }
+
+ val paramCardBalanceState: AnalyticsParam.CardBalanceState by lazy { calculateAmount().toCardBalanceState() }
+
+ private fun calculateAmount(): BigDecimal {
+ return biometricsWalletDataModels?.calculateTotalCryptoAmount()
+ ?: walletState.walletsDataFromStores.calculateTotalCryptoAmount()
+ }
+
+ private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when {
+ isZero() -> AnalyticsParam.CardBalanceState.Empty
+ else -> AnalyticsParam.CardBalanceState.Full
+ }
+
+ private fun List.calculateTotalCryptoAmount(): BigDecimal {
+ return this
+ .map { it.status.amount }
+ .reduce(BigDecimal::plus)
+ }
+}
+
+class BasicSignInEventConverter : Converter {
+
+ override fun convert(value: BasicEventsSourceData): Basic.SignedIn? {
+ if (value.paramCardCurrency == null || value.userWalletIdStringValue == null) return null
return Basic.SignedIn(
- state = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores),
- currency = cardCurrency,
- batch = scanResponse.card.batchId,
+ state = value.paramCardBalanceState,
+ currency = value.paramCardCurrency!!,
+ batch = value.batchId,
).apply {
- filterData = UserWalletIdBuilder.scanResponse(scanResponse)
- .build()
- ?.stringValue
+ filterData = value.userWalletIdStringValue
}
}
}
-class BasicTopUpEventConverter(
- private val scanResponse: ScanResponse,
-) : Converter {
+class BasicTopUpEventConverter : Converter {
- override fun convert(value: WalletState): Basic.ToppedUp? {
- if (!statesIsReadyToCreateEvent(scanResponse.cardTypesResolver, value)) return null
- val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) ?: return null
+ override fun convert(value: BasicEventsSourceData): Basic.ToppedUp? {
+ if (value.paramCardCurrency == null || value.userWalletIdStringValue == null) return null
val data = BasicTopUpFilter.Data(
- walletId = UserWalletIdBuilder.scanResponse(scanResponse).build()?.stringValue ?: "",
- cardBalanceState = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores),
+ walletId = value.userWalletIdStringValue!!,
+ cardBalanceState = value.paramCardBalanceState,
)
- return Basic.ToppedUp(cardCurrency).apply { filterData = data }
+ return Basic.ToppedUp(value.paramCardCurrency!!).apply { filterData = data }
}
-}
-
-private fun AnalyticsParam.CardBalanceState.Companion.from(
- walletsData: List,
-): AnalyticsParam.CardBalanceState {
- val totalCryptoAmount = walletsData.calculateTotalCryptoAmount()
- return when {
- totalCryptoAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty
- else -> AnalyticsParam.CardBalanceState.Full
- }
-}
-
-private fun statesIsReadyToCreateEvent(cardTypesResolver: CardTypesResolver, state: WalletState): Boolean {
- if (cardTypesResolver.isMultiwalletAllowed() && state.missingDerivations.isNotEmpty()) return false
- if (state.walletsDataFromStores.isEmpty()) return false
-
- val totalBalanceState = state.totalBalance?.state ?: return false
- if (totalBalanceState == ProgressState.Loading || totalBalanceState == ProgressState.Refreshing) return false
-
- val balancesCount = state.walletsDataFromStores.map {
- if (it.currencyData.amount == null) 0 else 1
- }.reduce { acc, i -> acc + i }
-
- if (balancesCount != state.walletsStores.size) return false
-
- return true
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt
index 95ec22a8aa..61d71c5a14 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt
@@ -18,6 +18,7 @@ class ParamCardCurrencyConverter : Converter AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain())
value.isTangemTwins() -> AnalyticsParam.CurrencyType.Blockchain(Blockchain.Bitcoin)
value.isSaltPay() -> AnalyticsParam.CurrencyType.Token(SaltPayWorkaround.tokenFrom(Blockchain.SaltPay))
+ value.getBlockchain() != Blockchain.Unknown -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain())
value.getPrimaryToken() != null -> AnalyticsParam.CurrencyType.Token(value.getPrimaryToken()!!)
else -> null
} ?: return null
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt
index ddbd1683bd..49f6762db9 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt
@@ -8,19 +8,19 @@ import com.tangem.tap.common.shop.data.ProductType
/**
[REDACTED_AUTHOR]
*/
-class ShopOrderToEventConverter : Converter, Shop.Purchased> {
+class ShopOrderToEventConverter : Converter, Shop.Purchased> {
- override fun convert(value: Pair): Shop.Purchased {
- val order = value.first
+ override fun convert(value: Pair): Shop.Purchased {
+ val checkout = value.first
val productType = value.second
- val sku = order.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: productType.sku
+ val sku = checkout.lineItems?.edges?.firstOrNull()?.node?.variant?.sku ?: productType.sku
val count = when (productType) {
ProductType.WALLET_2_CARDS -> "2"
ProductType.WALLET_3_CARDS -> "3"
}
- val amount = "${order.totalPriceV2.amount} ${order.totalPriceV2.currencyCode.name}"
- val code = (order.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code
+ val amount = "${checkout.totalPriceV2.amount} ${checkout.totalPriceV2.currencyCode.name}"
+ val code = (checkout.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code
return Shop.Purchased(sku, count, amount, code)
}
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt
index 2b660fa11d..4db0526ad2 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt
@@ -7,14 +7,14 @@ sealed class MyWallets(
params: Map = mapOf(),
) : AnalyticsEvent("My Wallets", event, params) {
- object MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened")
- object CardWasScanned : MyWallets(event = "Card Was Scanned")
- object WalletUnlockTapped : MyWallets(event = "Wallet Unlock Tapped")
+ class MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened")
+ class CardWasScanned : MyWallets(event = "Card Was Scanned")
- object Button {
- object ScanNewCard : MyWallets(event = "Button - Scan New Card")
- object UnlockWithBiometrics : MyWallets(event = "Button - Unlock all with Face ID")
- object EditWalletTapped : MyWallets(event = "Button - Edit Wallet Tapped")
- object DeleteWalletTapped : MyWallets(event = "Button - Delete Wallet Tapped")
+ sealed class Button {
+ class ScanNewCard : MyWallets(event = "Button - Scan New Card")
+ class UnlockWithBiometrics : MyWallets(event = "Button - Unlock all with Face ID")
+ class EditWalletTapped : MyWallets(event = "Button - Edit Wallet Tapped")
+ class DeleteWalletTapped : MyWallets(event = "Button - Delete Wallet Tapped")
+ class WalletUnlockTapped : MyWallets(event = "Button - Wallet Unlock Tapped")
}
}
\ No newline at end of file
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 91a3ae0741..8eb0f6490d 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
@@ -75,6 +75,8 @@ sealed class Onboarding(
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 EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding(
category = "Onboarding / Biometric",
event = "Enable Biometric",
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt
index c9b0419a49..03cbb88ac6 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt
@@ -21,6 +21,7 @@ sealed class Settings(
class ButtonCardSettings : Settings(event = "Button - Card Settings")
class ButtonAppSettings : Settings(event = "Button - App Settings")
class ButtonCreateBackup : Settings(event = "Button - Create Backup")
+ class ButtonWalletConnect : Settings(event = "Button - Wallet Connect")
class ButtonSocialNetwork(network: SocialNetwork) : Settings(
event = "Button - Social Network",
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt
index 788f557537..0ab4dd9361 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Shop.kt
@@ -11,7 +11,7 @@ sealed class Shop(
params: Map = mapOf(),
) : AnalyticsEvent("Shop", event, params) {
- class ScreenOpened : IntroductionProcess("Shop Screen Opened")
+ class ScreenOpened : Shop("Shop Screen Opened")
class Purchased(sku: String, count: String, amount: String, couponCode: String?) : Shop(
event = "Purchased",
@@ -23,8 +23,8 @@ sealed class Shop(
).filterNotNull(),
)
- class Redirected(partnerName: String) : Shop(
+ class Redirected(partnerName: String?) : Shop(
event = "Redirected",
- params = mapOf("Partner" to partnerName),
+ params = partnerName?.let { mapOf("Partner" to partnerName) } ?: mapOf(),
)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt
new file mode 100644
index 0000000000..cad34430d9
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt
@@ -0,0 +1,19 @@
+package com.tangem.tap.common.analytics.events
+
+import com.tangem.core.analytics.AnalyticsEvent
+
+/**
+[REDACTED_AUTHOR]
+ */
+sealed class SignIn(
+ event: String,
+ params: Map = mapOf(),
+ error: Throwable? = null,
+) : AnalyticsEvent("Sign In", event, params, error) {
+
+ class ScreenOpened : SignIn(event = "Sing In Screen Opened")
+ class CardWasScanned : SignIn(event = "Card Was Scanned")
+
+ class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In")
+ class ButtonCardSignIn : SignIn(event = "Button - Card Sign In")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt
index 99badb9998..0ad3ca514e 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt
@@ -46,14 +46,14 @@ sealed class Token(
params = mapOf("Token" to type.value),
)
- sealed class Recieve(
+ sealed class Receive(
event: String,
params: Map = mapOf(),
- ) : Token("Token / Recieve", event, params) {
+ ) : Token("Token / Receive", event, params) {
- class ScreenOpened : Recieve("Recieve Screen Opened")
- class ButtonCopyAddress : Recieve("Button - Copy Address")
- class ButtonShareAddress : Recieve("Button - Share Address")
+ class ScreenOpened : Receive("Receive Screen Opened")
+ class ButtonCopyAddress : Receive("Button - Copy Address")
+ class ButtonShareAddress : Receive("Button - Share Address")
}
sealed class Send(
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt
index b3e4b835be..9ce10213ac 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt
@@ -11,6 +11,7 @@ sealed class WalletConnect(
error: Throwable? = null,
) : AnalyticsEvent("Wallet Connect", event, params, error) {
+ class ScreenOpened : WalletConnect(event = "WC Screen Opened")
class NewSessionEstablished : WalletConnect("New Session Established")
class SessionDisconnected : WalletConnect("Session Disconnected")
class RequestSigned : WalletConnect("Request Signed")
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/filters/ShopPurchasedEventFilter.kt b/app/src/main/java/com/tangem/tap/common/analytics/filters/ShopPurchasedEventFilter.kt
deleted file mode 100644
index 738d3fed8f..0000000000
--- a/app/src/main/java/com/tangem/tap/common/analytics/filters/ShopPurchasedEventFilter.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.tangem.tap.common.analytics.filters
-
-import com.tangem.core.analytics.api.AnalyticsEventFilter
-import com.tangem.core.analytics.api.AnalyticsHandler
-import com.tangem.core.analytics.AnalyticsEvent
-import com.tangem.tap.common.analytics.events.Shop
-import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
-import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
-
-/**
-[REDACTED_AUTHOR]
- */
-class ShopPurchasedEventFilter : AnalyticsEventFilter {
-
- override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Shop.Purchased
-
- override fun canBeSent(event: AnalyticsEvent): Boolean = true
-
- override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean {
- return when (handler) {
- is AppsFlyerAnalyticsHandler, is FirebaseAnalyticsHandler -> true
- else -> false
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt
index a0b6cb378f..d1b3189ab1 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt
@@ -1,7 +1,10 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer
+import com.appsflyer.AFInAppEventType
+import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
+import com.tangem.tap.common.analytics.events.Shop
class AppsFlyerAnalyticsHandler(
private val client: AppsFlyerAnalyticsClient,
@@ -13,6 +16,14 @@ class AppsFlyerAnalyticsHandler(
client.logEvent(event, params)
}
+ override fun send(event: AnalyticsEvent) {
+ if (event is Shop.Purchased) {
+ send(AFInAppEventType.PURCHASE, event.params)
+ } else {
+ super.send(event)
+ }
+ }
+
companion object {
const val ID = "AppsFlyer"
}
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt
index 2172fe9aee..7430fcc17f 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt
@@ -1,10 +1,12 @@
package com.tangem.tap.common.analytics.handlers.firebase
+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.ErrorEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
+import com.tangem.tap.common.analytics.events.Shop
class FirebaseAnalyticsHandler(
private val client: FirebaseAnalyticsClient,
@@ -18,17 +20,23 @@ class FirebaseAnalyticsHandler(
override fun send(event: AnalyticsEvent) {
val error = event.error
- if (error == null) {
- super.send(event)
- } else {
- val errorConverter = AnalyticsErrorConverter()
- if (!errorConverter.canBeHandled(error)) return
+ when {
+ error != null -> {
+ val errorConverter = AnalyticsErrorConverter()
+ if (!errorConverter.canBeHandled(error)) return
- val errorParams = errorConverter.convert(error).toMutableMap()
- errorParams["Category"] = event.category
- errorParams["Event"] = event.event
- errorParams.putAll(event.params)
- send(error, errorParams)
+ val errorParams = errorConverter.convert(error).toMutableMap()
+ errorParams["Category"] = event.category
+ errorParams["Event"] = event.event
+ errorParams.putAll(event.params)
+ send(error, errorParams)
+ }
+ event is Shop.Purchased -> {
+ send(FirebaseAnalytics.Event.PURCHASE, event.params)
+ }
+ else -> {
+ super.send(event)
+ }
}
}
diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
index a2f9dc5f7b..ff4f898d8d 100644
--- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
+++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt
@@ -11,7 +11,7 @@ import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.*
-// todo move extensions to utils
+// TODO: move extensions to utils
fun BigDecimal.toFormattedString(
decimals: Int,
roundingMode: RoundingMode = RoundingMode.DOWN,
@@ -75,7 +75,7 @@ fun BigDecimal.toFormattedFiatValue(
): String {
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
- return " $fiatValue $fiatCurrencyName"
+ return " $fiatValue $fiatCurrencyName"
}
fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString()
diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt
index 12fe1e42e9..b44db4004b 100644
--- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt
+++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt
@@ -2,6 +2,7 @@ package com.tangem.tap.common.feedback
import android.content.Context
import android.os.Build
+import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds
import com.tangem.tap.common.extensions.sendEmail
import com.tangem.tap.common.log.TangemLogCollector
@@ -108,6 +109,7 @@ class FeedbackManager(
}
private fun showMessagingActivity(context: Context) {
+ Analytics.send(com.tangem.tap.common.analytics.events.Chat.ScreenOpened())
MessagingActivity.builder()
.withMultilineResponseOptionsEnabled(false)
.withBotLabelStringRes(R.string.chat_bot_name)
diff --git a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt
index 4408155f8e..8366078322 100644
--- a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt
+++ b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt
@@ -202,8 +202,8 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
suspend fun waitForCheckout(productType: ProductType) {
val result = shopifyService.checkout(true, checkouts[productType]!!.id)
result.onSuccess { checkout ->
- checkout.order?.let {
- val event = ShopOrderToEventConverter().convert(it to productType)
+ if (checkout.order != null && checkout.lineItems != null) {
+ val event = ShopOrderToEventConverter().convert(checkout to productType)
Analytics.send(event)
}
}
diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
index 82f2159279..dd13b37e8d 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
@@ -29,6 +29,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.wallet.models.toBlockchainNetworks
import com.tangem.tap.features.wallet.redux.WalletAction
+import com.tangem.tap.features.wallet.redux.middlewares.handleBasicAnalyticsEvent
import com.tangem.tap.network.NetworkConnectivity
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
@@ -123,6 +124,7 @@ class TapWalletManager {
.doOnSuccess {
Timber.d("Wallet stores fetched for ${userWallet.walletId}")
store.dispatchOnMain(WalletAction.LoadData.Success)
+ handleBasicAnalyticsEvent()
}
.doOnFailure { error ->
val errorAction = when (error) {
@@ -200,6 +202,7 @@ class TapWalletManager {
}
if (data.cardTypesResolver.isMultiwalletAllowed()) {
+ dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
loadMultiWalletData(data)
} else {
loadSingleWalletData(data)
@@ -222,9 +225,7 @@ class TapWalletManager {
.filter {
it.derivationPath != null && !scanResponse.hasDerivation(it.blockchain, it.derivationPath)
}
- if (missingDerivations.isNotEmpty()) {
- store.dispatch(WalletAction.MultiWallet.AddMissingDerivations(missingDerivations))
- }
+ store.dispatch(WalletAction.MultiWallet.AddMissingDerivations(missingDerivations))
}
private suspend fun loadSingleWalletData(data: ScanResponse) {
@@ -276,7 +277,9 @@ class TapWalletManager {
}
suspend fun reloadData(data: ScanResponse) {
- loadUserCurrencies(data, walletManagerFactory)
+ if (data.cardTypesResolver.isMultiwalletAllowed()) {
+ loadUserCurrencies(data, walletManagerFactory)
+ }
withContext(Dispatchers.Main) {
getActionIfUnknownBlockchainOrEmptyWallet(data)?.let {
store.dispatch(it)
diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt
index 4462f4d8ec..3429afa07c 100644
--- a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt
+++ b/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt
@@ -14,6 +14,7 @@ import java.math.BigDecimal
* @param existentialDeposit Amount that must be held on currency's balance, if balance is below that amount all
* founds will be destroyed. Null if currency don't have existential deposit
* @param fiatRate Wallet's fiat rate, used to calculate fiat balance. Null if not provided
+ * @param isCardSingleToken shows that [Currency] is a card token
* */
data class WalletDataModel(
val currency: Currency,
@@ -21,6 +22,7 @@ data class WalletDataModel(
val walletAddresses: List,
val existentialDeposit: BigDecimal?,
val fiatRate: BigDecimal?,
+ val isCardSingleToken: Boolean,
) {
/**
diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt
index a0511784e2..7bca1028c0 100644
--- a/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt
+++ b/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt
@@ -6,10 +6,12 @@ import com.tangem.common.hdWallet.DerivationPath
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.domain.model.WalletStoreModel.WalletRent
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
+import com.tangem.tap.features.wallet.models.Currency
import java.math.BigDecimal
/**
* Contains info about the blockchain and its currencies
+ *
* @param userWalletId ID of the associated [UserWallet]
* @param blockchain [Blockchain] of this WalletStore
* @param derivationPath [DerivationPath] of this store, null if the card does not support the
@@ -21,6 +23,9 @@ import java.math.BigDecimal
* TODO: Remove after WalletMiddleware refactoring
* @param walletManager [WalletManager], may be null if it fails to create this manager.
* TODO: Remove after WalletMiddleware refactoring
+ *
+ * @property blockchainWalletData Returns the [WalletDataModel] of the blockchain of this wallet store
+ * or throw [NoSuchElementException] if this wallet store not contains [WalletDataModel] of the blockchain
* */
data class WalletStoreModel(
val userWalletId: UserWalletId,
@@ -34,6 +39,9 @@ data class WalletStoreModel(
val walletManager: WalletManager?,
) {
+ val blockchainWalletData: WalletDataModel
+ get() = walletsData.first { it.currency is Currency.Blockchain }
+
/**
* Represents wallet blockchain rent
* @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than
diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt
index ada8c6896e..b48ae715a2 100644
--- a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt
+++ b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt
@@ -97,6 +97,7 @@ private fun BlockchainNetwork.getBlockchainWalletData(walletManager: WalletManag
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
+ isCardSingleToken = false,
)
}
@@ -113,6 +114,7 @@ private fun BlockchainNetwork.getTokensWalletsData(walletManager: WalletManager?
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
+ isCardSingleToken = walletManager?.cardTokens?.contains(token) ?: false,
)
}
}
@@ -128,6 +130,7 @@ private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): Wal
walletAddresses = wallet.createAddressesData(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
+ isCardSingleToken = false,
)
}
@@ -143,6 +146,7 @@ private fun Token.toTokenWalletData(walletManager: WalletManager): WalletDataMod
walletAddresses = wallet.createAddressesData(),
existentialDeposit = getExistentialDeposit(walletManager),
fiatRate = null,
+ isCardSingleToken = walletManager.cardTokens.contains(this),
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt
index f8404f04ab..07e0eee2d4 100644
--- a/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt
+++ b/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt
@@ -7,17 +7,15 @@ import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
+import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.backup.BackupService
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.backupService
-import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
-import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa
-import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
@@ -30,6 +28,7 @@ import com.tangem.tap.features.onboarding.OnboardingSaltPayHelper
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayExceptionHandler
+import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
import com.tangem.tap.preferencesStorage
@@ -37,7 +36,6 @@ import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.tap.userTokensRepository
-import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -45,6 +43,7 @@ import kotlinx.coroutines.launch
// TODO: Create repository for that
object ScanCardProcessor {
suspend fun scan(
+ analyticsEvent: AnalyticsEvent? = null,
additionalBlockchainsToDerive: Collection? = null,
cardId: String? = null,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {},
@@ -69,7 +68,6 @@ object ScanCardProcessor {
result
.doOnFailure { error ->
- onProgressStateChange(false)
onScanStateChange(false)
onFailure(error)
}
@@ -77,14 +75,15 @@ object ScanCardProcessor {
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
onScanStateChange(false)
+ sendAnalytics(analyticsEvent, scanResponse.card.batchId)
+
checkForUnfinishedBackupForSaltPay(
backupService = backupService,
scanResponse = scanResponse,
- onProgressStateChange = { onProgressStateChange(it) },
+ onFailure = onFailure,
nextHandler = { scanResponse1 ->
showDisclaimerIfNeed(
scanResponse = scanResponse1,
- onProgressStateChange = onProgressStateChange,
disclaimerWillShow = disclaimerWillShow,
onFailure = onFailure,
nextHandler = { scanResponse2 ->
@@ -102,16 +101,24 @@ object ScanCardProcessor {
}
}
+ private fun sendAnalytics(
+ analyticsEvent: AnalyticsEvent?,
+ batchId: String,
+ ) {
+ Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId))
+ analyticsEvent?.let { Analytics.send(it) }
+ }
+
/**
* It checks only the SaltPay cards. To check for unfinished backups for the standard Wallet cards
* see BackupAction.CheckForUnfinishedBackup
* If user touches card other than Visa SaltPay - show dialog and block next processing
*/
- private inline fun checkForUnfinishedBackupForSaltPay(
+ private suspend inline fun checkForUnfinishedBackupForSaltPay(
backupService: BackupService,
scanResponse: ScanResponse,
- onProgressStateChange: (showProgress: Boolean) -> Unit,
nextHandler: (ScanResponse) -> Unit,
+ onFailure: suspend (error: TangemError) -> Unit,
) {
if (!backupService.hasIncompletedBackup || !backupService.primaryCardIsSaltPayVisa()) {
nextHandler(scanResponse)
@@ -123,8 +130,9 @@ object ScanCardProcessor {
?: false
if (scanResponse.cardTypesResolver.isSaltPayWallet() || !isTheSamePrimaryCard) {
- onProgressStateChange(false)
- showSaltPayTapVisaLogoCardDialog()
+ val error = SaltPayActivationError.PutVisaCard
+ SaltPayExceptionHandler.handle(error)
+ onFailure(TangemSdkError.ExceptionError(error))
} else {
nextHandler(scanResponse)
}
@@ -133,7 +141,6 @@ object ScanCardProcessor {
private suspend inline fun showDisclaimerIfNeed(
scanResponse: ScanResponse,
crossinline disclaimerWillShow: () -> Unit = {},
- crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
crossinline nextHandler: suspend (ScanResponse) -> Unit,
crossinline onFailure: suspend (error: TangemError) -> Unit,
) {
@@ -141,30 +148,27 @@ object ScanCardProcessor {
store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer))
if (disclaimer.isAccepted()) {
- nextHandler(scanResponse)
- } else {
- scope.launch {
- delay(DELAY_SDK_DIALOG_CLOSE)
- disclaimerWillShow()
- dispatchOnMain(
- DisclaimerAction.Show(
- fromScreen = AppScreen.Home,
- callback = DisclaimerCallback(
- onAccept = {
- scope.launch(Dispatchers.Main) {
- nextHandler(scanResponse)
- }
- },
- onDismiss = {
- scope.launch(Dispatchers.Main) {
- onProgressStateChange(false)
- onFailure(TangemSdkError.UserCancelled())
- }
- },
- ),
+ nextHandler((scanResponse))
+ } else scope.launch {
+ delay(DELAY_SDK_DIALOG_CLOSE)
+ disclaimerWillShow()
+ dispatchOnMain(
+ DisclaimerAction.Show(
+ fromScreen = AppScreen.Home,
+ callback = DisclaimerCallback(
+ onAccept = {
+ scope.launch(Dispatchers.Main) {
+ nextHandler(scanResponse)
+ }
+ },
+ onDismiss = {
+ scope.launch(Dispatchers.Main) {
+ onFailure(TangemSdkError.UserCancelled())
+ }
+ },
),
- )
- }
+ ),
+ )
}
}
@@ -176,14 +180,10 @@ object ScanCardProcessor {
crossinline onSuccess: suspend (ScanResponse) -> Unit,
crossinline onFailure: suspend (error: TangemError) -> Unit,
) {
- Analytics.send(IntroductionProcess.CardWasScanned())
-
val globalState = store.state.globalState
val tapWalletManager = globalState.tapWalletManager
tapWalletManager.updateConfigManager(scanResponse)
- Analytics.addParamsInterceptor(BatchIdParamsInterceptor(scanResponse.card.batchId))
-
store.dispatchOnMain(TwinCardsAction.IfTwinsPrepareState(scanResponse))
if (scanResponse.cardTypesResolver.isSaltPay()) {
@@ -204,25 +204,24 @@ object ScanCardProcessor {
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
onSuccess(scanResponse)
- onProgressStateChange(false)
}
}
is Result.Failure -> {
- SaltPayExceptionHandler.handle(result.error)
delay(DELAY_SDK_DIALOG_CLOSE)
+ SaltPayExceptionHandler.handle(result.error)
onFailure(TangemSdkError.ExceptionError(result.error))
- onProgressStateChange(false)
}
}
}
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
if (scanResponse.card.backupStatus?.isActive == false) {
- showSaltPayTapVisaLogoCardDialog()
+ val error = SaltPayActivationError.PutVisaCard
+ SaltPayExceptionHandler.handle(error)
+ onFailure(TangemSdkError.ExceptionError(error))
} else {
onSuccess(scanResponse)
}
- onProgressStateChange(false)
}
} else {
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
@@ -238,21 +237,11 @@ object ScanCardProcessor {
} else {
delay(DELAY_SDK_DIALOG_CLOSE)
onSuccess(scanResponse)
- onProgressStateChange(false)
}
}
}
}
- private fun showSaltPayTapVisaLogoCardDialog() {
- store.dispatchDialogShow(
- AppDialog.SimpleOkDialogRes(
- headerId = R.string.saltpay_error_empty_backup_title,
- messageId = R.string.saltpay_error_empty_backup_message,
- ),
- )
- }
-
private suspend inline fun navigateTo(
screen: AppScreen,
onProgressStateChange: (showProgress: Boolean) -> Unit,
diff --git a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt b/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt
index 12fd8abb1e..faa4628b9b 100644
--- a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt
+++ b/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt
@@ -48,7 +48,10 @@ internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator {
is WalletDataModel.SameCurrencyTransactionInProgress,
is WalletDataModel.TransactionInProgress,
is WalletDataModel.NoAccount,
- -> TotalFiatBalanceStatus.Loaded
+ -> {
+ if (walletData.fiatRate == null) TotalFiatBalanceStatus.Error
+ else TotalFiatBalanceStatus.Loaded
+ }
is WalletDataModel.Unreachable,
is WalletDataModel.MissedDerivation,
-> TotalFiatBalanceStatus.Error
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt
index ed773b522d..e4bebc2c07 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt
@@ -22,6 +22,7 @@ interface WalletStoresManager {
* @return [Flow] with [WalletStoreModel] list
* */
fun get(userWalletId: UserWalletId): Flow>
+ suspend fun getSync(userWalletId: UserWalletId): List
/**
* Delete [WalletStoreModel]s associated with provided [UserWalletId]s
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt
index 219d7f4af1..22e86d2fa1 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt
@@ -44,6 +44,10 @@ internal class DefaultWalletStoresManager(
.distinctUntilChanged()
}
+ override suspend fun getSync(userWalletId: UserWalletId): List {
+ return walletStoresRepository.getSync(userWalletId)
+ }
+
override suspend fun delete(userWalletsIds: List): CompletionResult {
return walletStoresRepository.delete(userWalletsIds)
.flatMap { walletManagersRepository.delete(userWalletsIds) }
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt
index b1729a0b11..7657f2d47b 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt
@@ -17,6 +17,10 @@ internal class DummyWalletStoresManager : WalletStoresManager {
return emptyFlow()
}
+ override suspend fun getSync(userWalletId: UserWalletId): List {
+ return emptyList()
+ }
+
override suspend fun delete(userWalletsIds: List): CompletionResult {
return CompletionResult.Success(Unit)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt
index 2739a8c2b1..a7dc0a853d 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt
@@ -26,6 +26,7 @@ import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository
import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStore
import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStores
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithAmounts
+import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithDemoAmounts
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithError
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithFiatRates
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithMissedDerivation
@@ -33,6 +34,7 @@ import com.tangem.tap.domain.walletStores.repository.implementation.utils.update
import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithUnreachable
import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage
import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage
+import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.models.filterByCoin
@@ -208,6 +210,8 @@ internal class DefaultWalletAmountsRepository(
updateWalletStoreWithAmounts(
walletStore = walletStore,
updatedWallet = walletManager.wallet,
+ // FIXME: move DemoHelper to Demo core module maybe
+ isDemo = DemoHelper.isDemoCardId(scanResponse.card.cardId),
)
}
.flatMap { fetchWalletStoreRentIfNeeded(walletStore, walletManager) }
@@ -297,6 +301,7 @@ internal class DefaultWalletAmountsRepository(
private suspend fun updateWalletStoreWithAmounts(
walletStore: WalletStoreModel,
updatedWallet: Wallet,
+ isDemo: Boolean,
) = withContext(Dispatchers.Default) {
Timber.d(
"""
@@ -310,7 +315,11 @@ internal class DefaultWalletAmountsRepository(
prevState.replaceWalletStore(
walletStoreToUpdate = walletStore,
update = {
- it.updateWithAmounts(wallet = updatedWallet)
+ if (isDemo) {
+ it.updateWithDemoAmounts(wallet = updatedWallet)
+ } else {
+ it.updateWithAmounts(wallet = updatedWallet)
+ }
},
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt
index 64e2241737..cb49fe33c1 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt
@@ -7,6 +7,7 @@ import com.tangem.common.core.TangemError
import com.tangem.tap.domain.extensions.amountToCreateAccount
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.model.WalletDataModel
+import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.getPendingTransactions
import java.math.BigDecimal
@@ -74,12 +75,25 @@ internal fun WalletDataModel.updateWithAmount(wallet: Wallet): WalletDataModel {
)
}
+internal fun WalletDataModel.updateWithDemoAmount(wallet: Wallet): WalletDataModel {
+ val amount = DemoHelper.config.getBalance(wallet.blockchain)
+ return this.copy(
+ status = WalletDataModel.VerifiedOnline(amount = amount.value ?: BigDecimal.ZERO),
+ )
+}
+
internal fun List.updateWithAmounts(wallet: Wallet): List {
return this.map { walletData ->
walletData.updateWithAmount(wallet)
}
}
+internal fun List.updateWithDemoAmounts(wallet: Wallet): List {
+ return this.map { walletData ->
+ walletData.updateWithDemoAmount(wallet)
+ }
+}
+
internal fun WalletDataModel.updateWithError(
wallet: Wallet,
error: TangemError,
@@ -164,7 +178,7 @@ internal fun List.updateWithSelf(
val updatedWalletsData = arrayListOf()
newWalletsData.forEach { newWalletData ->
- val walletDataToUpdate = oldWalletsData.find(newWalletData::isSameWalletData)
+ val walletDataToUpdate = oldWalletsData.firstOrNull(newWalletData::isSameWalletData)
if (walletDataToUpdate != null) {
updatedWalletsData.add(walletDataToUpdate.updateWithSelf(newWalletData))
} else {
@@ -176,5 +190,5 @@ internal fun List.updateWithSelf(
}
internal fun WalletDataModel.isSameWalletData(other: WalletDataModel): Boolean {
- return currency == other.currency
+ return this.currency == other.currency
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt
index 0e9e9d8715..36d9eac8b2 100644
--- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt
+++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt
@@ -49,6 +49,14 @@ internal fun WalletStoreModel.updateWithAmounts(
)
}
+internal fun WalletStoreModel.updateWithDemoAmounts(
+ wallet: Wallet,
+): WalletStoreModel {
+ return this.copy(
+ walletsData = walletsData.updateWithDemoAmounts(wallet = wallet),
+ )
+}
+
internal fun WalletStoreModel.updateWithFiatRates(
rates: Map,
): WalletStoreModel {
@@ -96,26 +104,30 @@ private inline fun List.replaceWalletStores(
walletStoresToUpdate.forEach { walletStoreToUpdate ->
val index = mutableStores.indexOfFirst(walletStoreToUpdate::isSameWalletStore)
+ // Can be possible if user hides wallet store when it's tokens is loading
+ if (index == -1) return@forEach
+
val currentWalletStore = mutableStores[index]
val updatedWalletStore = update(currentWalletStore)
- if (currentWalletStore != updatedWalletStore) {
- Timber.d(
- """
- Update wallet store in storage
- |- User wallet ID: ${updatedWalletStore.userWalletId}
- |- Blockchain: ${updatedWalletStore.blockchain}
- """.trimIndent(),
- )
+ if (currentWalletStore != updatedWalletStore) {
+ Timber.d(
+ """
+ Update wallet store in storage
+ |- User wallet ID: ${updatedWalletStore.userWalletId}
+ |- Blockchain: ${updatedWalletStore.blockchain}
+ """.trimIndent(),
+ )
- mutableStores[index] = updatedWalletStore
+ mutableStores[index] = updatedWalletStore
+ }
}
- }
return mutableStores
}
internal fun WalletStoreModel.isSameWalletStore(other: WalletStoreModel): Boolean {
- return this.blockchain == other.blockchain &&
+ return this.userWalletId == other.userWalletId &&
+ this.blockchain == other.blockchain &&
this.derivationPath == other.derivationPath
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
index 757011d676..203af20870 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
@@ -140,8 +140,8 @@ class DetailsMiddleware {
}
}
.doOnFailure { error ->
- (error as? TangemSdkError)?.let { sdkError ->
- Analytics.send(Settings.CardSettings.FactoryResetFinished(sdkError))
+ if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
+ Analytics.send(Settings.CardSettings.FactoryResetFinished(error))
}
}
}
@@ -179,7 +179,8 @@ class DetailsMiddleware {
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success)
}
is CompletionResult.Failure -> {
- (result.error as? TangemSdkError)?.let { error ->
+ val error = result.error
+ if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue, error))
}
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt
index ac69fe0ad8..fcdc6689cf 100644
--- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt
@@ -12,7 +12,7 @@ data class DetailsScreenState(
val onItemsClick: (SettingsElement) -> Unit,
val onSocialNetworkClick: (SocialNetworkLink) -> Unit,
) {
- val appNameRes: Int = R.string.app_name
+ val appNameRes: Int = R.string.tangem_app_name
}
@Immutable
@@ -44,7 +44,7 @@ sealed class SocialNetwork(val id: String, val iconRes: Int) {
object Facebook : SocialNetwork("Facebook", R.drawable.ic_facebook)
object Instagram : SocialNetwork("Instagram", R.drawable.ic_instagram)
object GitHub : SocialNetwork("GitHub", R.drawable.ic_github)
- object Youtube : SocialNetwork("Youtube", R.drawable.ic_youtube)
+ object YouTube : SocialNetwork("YouTube", R.drawable.ic_youtube)
object LinkedIn : SocialNetwork("LinkedIn", R.drawable.ic_linkedin)
}
@@ -55,7 +55,7 @@ object TangemSocialAccounts {
SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"),
SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"),
SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"),
- SocialNetworkLink(SocialNetwork.Youtube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
+ SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"),
)
val accountsRu: List = listOf(
@@ -64,7 +64,7 @@ object TangemSocialAccounts {
SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"),
SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"),
SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"),
- SocialNetworkLink(SocialNetwork.Youtube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
+ SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"),
SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"),
)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt
index cd4f28fe78..2f46d7e7f8 100644
--- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt
@@ -62,6 +62,7 @@ class DetailsViewModel(private val store: Store) {
private fun handleClickingSettingsItem(item: SettingsElement) {
when (item) {
SettingsElement.WalletConnect -> {
+ Analytics.send(Settings.ButtonWalletConnect())
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
}
SettingsElement.Chat -> {
diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt
index c6adeb7177..3ed76fdee2 100644
--- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt
@@ -9,7 +9,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
+import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
@@ -23,6 +25,7 @@ class WalletConnectFragment : Fragment(), StoreSubscriber {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ Analytics.send(WalletConnect.ScreenOpened())
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(android.R.transition.fade)
exitTransition = inflater.inflateTransition(android.R.transition.fade)
diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
index 0e0dc87965..f01fdaeb7c 100644
--- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt
@@ -83,7 +83,7 @@ class HomeFragment : Fragment(), StoreSubscriber {
homeState,
onScanButtonClick = {
Analytics.send(IntroductionProcess.ButtonScanCard())
- store.dispatch(HomeAction.ReadCard)
+ store.dispatch(HomeAction.ReadCard())
},
onShopButtonClick = {
Analytics.send(IntroductionProcess.ButtonBuyCards())
diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt
index eca85a7f61..95049be464 100644
--- a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt
@@ -97,6 +97,7 @@ fun RowScope.ProgressButton(
.weight(1f)
.height(48.dp),
onClick = onClick,
+ enabled = !inProgress,
colors = ButtonDefaults.textButtonColors(
backgroundColor = backgroundColor,
contentColor = contentColor,
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
index 6b71aee0f7..59e169ee04 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt
@@ -1,15 +1,18 @@
package com.tangem.tap.features.home.redux
+import com.tangem.core.analytics.AnalyticsEvent
+import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.entities.IndeterminateProgressButton
import org.rekotlin.Action
sealed class HomeAction : Action {
object Init : HomeAction()
- data class ShouldScanCardOnResume(val shouldScanCard: Boolean) : HomeAction()
- // from ui
- object ReadCard : HomeAction()
+ data class ReadCard(
+ val analyticsEvent: AnalyticsEvent? = IntroductionProcess.CardWasScanned(),
+ ) : HomeAction()
+
data class ScanInProgress(val scanInProgress: Boolean) : HomeAction()
data class GoToShop(val userCountryCode: String?) : HomeAction()
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
index 0e98babe31..44dbb45fda 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt
@@ -3,12 +3,14 @@ package com.tangem.tap.features.home.redux
import com.tangem.common.doOnFailure
import com.tangem.common.doOnResult
import com.tangem.common.doOnSuccess
+import com.tangem.core.analytics.Analytics
+import com.tangem.core.analytics.AnalyticsEvent
+import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.onUserWalletSelected
-import com.tangem.tap.common.postUiDelayBg
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
@@ -52,14 +54,11 @@ private fun handleHomeAction(action: Action) {
store.dispatch(GlobalAction.ExchangeManager.Init)
store.dispatch(GlobalAction.FetchUserCountry)
}
- is HomeAction.ShouldScanCardOnResume -> {
- if (action.shouldScanCard) {
- store.dispatch(HomeAction.ShouldScanCardOnResume(false))
- postUiDelayBg(ms = 700) { store.dispatch(HomeAction.ReadCard) }
- }
+ is HomeAction.ReadCard -> {
+ readCard(action.analyticsEvent)
}
- is HomeAction.ReadCard -> readCard()
is HomeAction.GoToShop -> {
+ Analytics.send(Shop.ScreenOpened())
when (action.userCountryCode) {
RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL)
else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop))
@@ -68,19 +67,19 @@ private fun handleHomeAction(action: Action) {
}
}
-private fun readCard() = scope.launch {
+private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
delay(timeMillis = 200)
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
+ analyticsEvent = analyticsEvent,
onProgressStateChange = { showProgress ->
if (showProgress) {
changeButtonState(ButtonState.PROGRESS)
+ } else {
+ changeButtonState(ButtonState.ENABLED)
}
- // else { //todo hide this because
- // changeButtonState(ButtonState.ENABLED)
- // }
},
onScanStateChange = { scanInProgress ->
store.dispatch(HomeAction.ScanInProgress(scanInProgress))
@@ -92,25 +91,32 @@ private fun readCard() = scope.launch {
scope.launch {
if (preferencesStorage.shouldSaveUserWallets) {
val userWallet = UserWalletBuilder(scanResponse).build() ?: return@launch
- userWalletsListManager.save(userWallet).doOnFailure { error ->
- Timber.e(error, "Unable to save user wallet")
- store.onCardScanned(scanResponse)
- }.doOnSuccess {
- scope.launch { store.onUserWalletSelected(userWallet) }
- }.doOnResult {
- changeButtonState(ButtonState.ENABLED)
- store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
- }
+ userWalletsListManager.save(userWallet)
+ .doOnFailure { error ->
+ Timber.e(error, "Unable to save user wallet")
+ store.onCardScanned(scanResponse)
+ }
+ .doOnSuccess {
+ scope.launch { store.onUserWalletSelected(userWallet) }
+ }
+ .doOnResult {
+ navigateTo(AppScreen.Wallet)
+ }
} else {
store.onCardScanned(scanResponse)
- changeButtonState(ButtonState.ENABLED)
- store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
+ navigateTo(AppScreen.Wallet)
}
}
},
)
}
+private suspend fun navigateTo(appScreen: AppScreen) {
+ store.dispatchOnMain(NavigationAction.NavigateTo(appScreen))
+ delay(200)
+ changeButtonState(ButtonState.ENABLED)
+}
+
private fun changeButtonState(state: ButtonState) {
store.dispatchOnMain(HomeAction.ChangeScanCardButtonState(IndeterminateProgressButton(state)))
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
index a998d9a359..f9f2911c32 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt
@@ -15,9 +15,6 @@ private fun internalReduce(action: Action, state: AppState): HomeState {
is HomeAction.ScanInProgress -> {
state = state.copy(scanInProgress = action.scanInProgress)
}
- is HomeAction.ShouldScanCardOnResume -> {
- state = state.copy(shouldScanCardOnResume = action.shouldScanCard)
- }
is HomeAction.ChangeScanCardButtonState -> {
state = state.copy(btnScanState = action.state)
}
diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
index 9ef6ea2b57..618e432dec 100644
--- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
+++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt
@@ -6,7 +6,6 @@ import com.tangem.tap.features.wallet.redux.ProgressState
import org.rekotlin.StateType
data class HomeState(
- val shouldScanCardOnResume: Boolean = false,
val scanInProgress: Boolean = false,
val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED),
) : StateType {
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt
index 0372ae6ee4..66e30fe595 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt
@@ -42,7 +42,7 @@ class AddressInfoBottomSheetDialog(
override fun show() {
super.show()
- Analytics.send(Token.Recieve.ScreenOpened())
+ Analytics.send(Token.Receive.ScreenOpened())
showData(data = stateDialog.addressData)
}
@@ -54,12 +54,12 @@ class AddressInfoBottomSheetDialog(
imvQrCode.setImageBitmap(data.qrCode)
tvAddress.text = data.address
btnFlCopyAddress.setOnClickListener {
- Analytics.send(Token.Recieve.ButtonCopyAddress())
+ Analytics.send(Token.Receive.ButtonCopyAddress())
context.copyToClipboard(data.address)
store.dispatchToastNotification(R.string.copy_toast_msg)
}
btnFlShare.setOnClickListener {
- Analytics.send(Token.Recieve.ButtonShareAddress())
+ Analytics.send(Token.Receive.ButtonShareAddress())
store.dispatchShare(data.shareUrl)
}
val blockchain = stateDialog.currency.blockchain
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt
index 2fff1d0fab..4af057eb58 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt
@@ -4,6 +4,8 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
+import com.tangem.core.analytics.Analytics
+import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.feedback.SupportInfo
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
@@ -19,6 +21,7 @@ class OnboardingMenuProvider : MenuProvider {
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.menu_item_chat_support -> {
+ Analytics.send(Onboarding.ButtonChat())
store.dispatch(GlobalAction.OpenChat(SupportInfo()))
true
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt
index 4538cb5145..152bbcc49e 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt
@@ -175,16 +175,16 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
val topUpUrl = walletManager.getTopUpUrl() ?: return
val blockchain = walletManager.wallet.blockchain
- if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) {
- val dialogData = WalletDialog.RussianCardholdersWarningDialog.Data(topUpUrl, blockchain)
- store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(dialogData))
- return
- }
val currencyType = AnalyticsParam.CurrencyType.Blockchain(blockchain)
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
- store.dispatchOpenUrl(topUpUrl)
+ if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) {
+ val dialogData = WalletDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
+ store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(dialogData))
+ } else {
+ store.dispatchOpenUrl(topUpUrl)
+ }
}
is OnboardingNoteAction.Done -> {
store.dispatch(GlobalAction.Onboarding.Stop)
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 01290fcdad..e9cb977dc5 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,6 +1,5 @@
package com.tangem.tap.features.onboarding.products.twins.redux
-import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
@@ -10,6 +9,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
+import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
@@ -27,8 +27,11 @@ 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.features.onboarding.OnboardingHelper
+import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
+import com.tangem.tap.features.wallet.redux.WalletAction
+import com.tangem.tap.features.wallet.redux.models.WalletDialog
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
@@ -103,10 +106,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
CreateTwinWalletMode.CreateWallet -> {
if (preferencesStorage.wasTwinsOnboardingShown()) {
val step = when {
- !scanResponse.twinsIsTwinned() -> {
- Analytics.send(Onboarding.CreateWallet.ScreenOpened())
- TwinCardsStep.CreateFirstWallet
- }
+ !scanResponse.twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet
twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done
else -> TwinCardsStep.TopUpWallet
}
@@ -147,18 +147,23 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
Analytics.send(Onboarding.Twins.ScreenOpened())
preferencesStorage.saveTwinsOnboardingShown()
}
- TwinCardsStep.TopUpWallet -> store.dispatch(TwinCardsAction.Balance.Update)
- TwinCardsStep.Done -> {
- finishCardActivation()
- postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) }
- }
TwinCardsStep.CreateFirstWallet -> {
+ Analytics.send(Onboarding.CreateWallet.ScreenOpened())
+ Analytics.send(Onboarding.Twins.SetupStarted())
scope.launch {
userWalletsListManager.delete(
listOfNotNull(UserWalletIdBuilder.scanResponse(getScanResponse()).build()),
)
}
}
+ TwinCardsStep.TopUpWallet -> {
+ Analytics.send(Onboarding.Topup.ScreenOpened())
+ store.dispatch(TwinCardsAction.Balance.Update)
+ }
+ TwinCardsStep.Done -> {
+ finishCardActivation()
+ postUi(500) { store.dispatch(TwinCardsAction.Confetti.Show) }
+ }
TwinCardsStep.None,
TwinCardsStep.Warning,
TwinCardsStep.CreateSecondWallet,
@@ -167,7 +172,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
}
is TwinCardsAction.Wallet.LaunchFirstStep -> {
- Analytics.send(Onboarding.Twins.SetupStarted())
val manager = TwinCardsManager(
card = getScanResponse().card,
assetReader = action.reader,
@@ -288,12 +292,23 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
store.dispatchDialogShow(appDialog)
}
is TwinCardsAction.TopUp -> {
- val topUpUrl = twinCardsState.walletManager?.getTopUpUrl() ?: return
+ val walletManager = twinCardsState.walletManager.guard {
+ store.dispatchDebugErrorNotification("NPE: WalletManager")
+ return
+ }
- val currencyType = AnalyticsParam.CurrencyType.Blockchain(Blockchain.Bitcoin)
+ val topUpUrl = walletManager.getTopUpUrl() ?: return
+ val blockchain = walletManager.wallet.blockchain
+
+ val currencyType = AnalyticsParam.CurrencyType.Blockchain(blockchain)
Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
- store.dispatchOpenUrl(topUpUrl)
+ if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) {
+ val dialogData = WalletDialog.RussianCardholdersWarningDialog.Data(topUpUrl)
+ store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(dialogData))
+ } else {
+ store.dispatchOpenUrl(topUpUrl)
+ }
}
TwinCardsAction.Done -> {
val scanResponse = getScanResponse()
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 a8e20e6640..8b7db136ec 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
@@ -154,7 +154,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
if (scanResponse == null) {
store.dispatch(NavigationAction.PopBackTo())
- store.dispatch(HomeAction.ReadCard)
+ store.dispatch(HomeAction.ReadCard())
} else {
val backupState = store.state.onboardingWalletState.backupState
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/GnosisRegistrator.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/GnosisRegistrator.kt
index 3a17f07fb7..8921be37f3 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/GnosisRegistrator.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/GnosisRegistrator.kt
@@ -38,7 +38,7 @@ class GnosisRegistrator(
}
private val otpProcessorContractAddress: String = when (walletManager.wallet.blockchain) {
- Blockchain.SaltPay -> "0x3B4397C817A26521Df8bD01a949AFDE2251d91C2"
+ Blockchain.SaltPay -> "0xc659f4FEd7A84a188F54cBA4A7a49D77c1a20522"
else -> throw IllegalArgumentException("GnosisRegistrator supports only the SaltPay blockchain")
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/SaltPayExceptionHandler.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/SaltPayExceptionHandler.kt
index cf34a759d4..0f0b207431 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/SaltPayExceptionHandler.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/SaltPayExceptionHandler.kt
@@ -11,33 +11,34 @@ import com.tangem.tap.store
/**
[REDACTED_AUTHOR]
*/
-object SaltPayExceptionHandler {
- fun handle(throwable: Throwable) {
- when (throwable) {
- is SaltPayActivationError -> {
- val dialog = when (throwable) {
- is SaltPayActivationError.NoGas -> SaltPayDialog.Activation.NoGas
- else -> SaltPayDialog.Activation.OnError(throwable)
- }
- store.dispatchDialogShow(dialog)
- }
- is TangemSdkError -> {
- when (throwable) {
- is TangemSdkError.NetworkError -> {
- val message = throwable.customMessage
- store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(message))
+class SaltPayExceptionHandler {
+ companion object {
+ fun handle(throwable: Throwable) {
+ when (throwable) {
+ is SaltPayActivationError -> {
+ val dialog = when (throwable) {
+ is SaltPayActivationError.NoGas -> SaltPayDialog.Activation.NoGas
+ is SaltPayActivationError.PutVisaCard -> SaltPayDialog.Activation.PutVisaCard
+ else -> SaltPayDialog.Activation.OnError(throwable)
}
- else -> {
- // do nothing
+ store.dispatchDialogShow(dialog)
+ }
+ is TangemSdkError -> {
+ when (throwable) {
+ is TangemSdkError.NetworkError -> {
+ val message = (throwable).customMessage
+ store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(message))
+ }
+ else -> Unit
}
}
- }
- is BlockchainSdkError -> {
- store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(throwable.customMessage))
- }
- else -> {
- val message = throwable.localizedMessage ?: "SaltPay unknown error"
- store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(message))
+ is BlockchainSdkError -> {
+ store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(throwable.customMessage))
+ }
+ else -> {
+ val message = throwable.localizedMessage ?: "SaltPay unknown error"
+ store.dispatchDialogShow(AppDialog.SimpleOkErrorDialog(message))
+ }
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/InterruptOnboardingDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/InterruptOnboardingDialog.kt
index 35a99173c3..1636675ae1 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/InterruptOnboardingDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/InterruptOnboardingDialog.kt
@@ -11,15 +11,17 @@ import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
-object InterruptOnboardingDialog {
- fun create(context: Context, dialog: OnboardingDialog.InterruptOnboarding): Dialog {
- return AlertDialog.Builder(context).apply {
- setTitle(context.getString(R.string.onboarding_exit_alert_title))
- setMessage(context.getString(R.string.onboarding_exit_alert_message))
- setPositiveButton(R.string.common_ok) { _, _ -> dialog.onOk() }
- setNegativeButton(R.string.common_cancel) { _, _ -> }
- setOnDismissListener { store.dispatchDialogHide() }
- setCancelable(false)
- }.create()
+class InterruptOnboardingDialog {
+ companion object {
+ fun create(context: Context, dialog: OnboardingDialog.InterruptOnboarding): Dialog {
+ return AlertDialog.Builder(context).apply {
+ setTitle(context.getString(R.string.onboarding_exit_alert_title))
+ setMessage(context.getString(R.string.onboarding_exit_alert_message))
+ setPositiveButton(R.string.common_ok) { _, _ -> dialog.onOk() }
+ setNegativeButton(R.string.common_cancel) { _, _ -> }
+ setOnDismissListener { store.dispatchDialogHide() }
+ setCancelable(false)
+ }.create()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/PutVisaCardDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/PutVisaCardDialog.kt
new file mode 100644
index 0000000000..ba689a1449
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/PutVisaCardDialog.kt
@@ -0,0 +1,27 @@
+package com.tangem.tap.features.onboarding.products.wallet.saltPay.dialog
+
+import android.app.Dialog
+import android.content.Context
+import androidx.appcompat.app.AlertDialog
+import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.store
+import com.tangem.wallet.R
+
+/**
+[REDACTED_AUTHOR]
+ */
+class PutVisaCardDialog {
+ companion object {
+ fun create(context: Context): Dialog {
+ return AlertDialog.Builder(context).apply {
+ setTitle(R.string.saltpay_error_empty_backup_title)
+ setMessage(R.string.saltpay_error_empty_backup_message)
+ setPositiveButton(R.string.common_ok) { _, _ -> }
+ setOnDismissListener {
+ store.dispatch(GlobalAction.HideDialog)
+ }
+ setCancelable(false)
+ }.create()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/SaltPayDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/SaltPayDialog.kt
index b8b512aef1..26228ba27f 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/SaltPayDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/dialog/SaltPayDialog.kt
@@ -9,6 +9,7 @@ import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPa
sealed class SaltPayDialog : StateDialog {
sealed class Activation : SaltPayDialog() {
object NoGas : SaltPayDialog()
+ object PutVisaCard : SaltPayDialog()
data class OnError(val error: SaltPayActivationError) : SaltPayDialog()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/message/SaltPayModuleMessage.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/message/SaltPayModuleMessage.kt
index a8e4a95885..592b888850 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/message/SaltPayModuleMessage.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/message/SaltPayModuleMessage.kt
@@ -49,6 +49,8 @@ sealed class SaltPayActivationError(
object NoFundsToClaim : SaltPayActivationError(subCode = 15)
object ClaimTransactionFailed : SaltPayActivationError(subCode = 16)
+ object PutVisaCard : SaltPayActivationError(18)
+
companion object {
const val EMPTY_MESSAGE = ""
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt
index a97cb49580..e25117369f 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt
@@ -285,7 +285,7 @@ suspend fun SaltPayActivationManager.update(
return Result.Failure(ex)
}
- // checkGasIfNeeded(this, newStep).successOr { return it }
+ checkGasIfNeeded(this, newStep).successOr { return it }
Timber.d("update: success: %s", newStep)
return Result.Success(newStep)
diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/ui/SaveWalletBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/ui/SaveWalletBottomSheetFragment.kt
index 80ed02da90..3f0f4f77c4 100644
--- a/app/src/main/java/com/tangem/tap/features/saveWallet/ui/SaveWalletBottomSheetFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/saveWallet/ui/SaveWalletBottomSheetFragment.kt
@@ -28,9 +28,9 @@ internal class SaveWalletBottomSheetFragment : ComposeBottomSheetFragment()
- override fun onDismiss(dialog: DialogInterface) {
- viewModel.dismiss()
- super.onDismiss(dialog)
+ override fun onCancel(dialog: DialogInterface) {
+ viewModel.cancelOrClose()
+ super.onCancel(dialog)
}
@Composable
@@ -51,7 +51,10 @@ internal class SaveWalletBottomSheetFragment : ComposeBottomSheetFragment {
+ Analytics.send(Shop.Redirected(null))
store.dispatchOpenUrl(shopService.getCheckoutUrl(shopState.selectedProduct))
store.dispatch(ShopAction.FinishSuccessfulOrder)
}
diff --git a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt
index 94d681c64e..47a769f3c6 100644
--- a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt
@@ -44,7 +44,6 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber<
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- Analytics.send(Shop.ScreenOpened())
activity?.onBackPressedDispatcher?.addCallback(
this,
diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/AddTokensFragment.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/AddTokensFragment.kt
index e8c8f62fc4..955d0e73fd 100644
--- a/app/src/main/java/com/tangem/tap/features/tokens/ui/AddTokensFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/AddTokensFragment.kt
@@ -49,7 +49,6 @@ class AddTokensFragment : BaseFragment(R.layout.fragment_add_tokens), StoreSubsc
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
- Analytics.send(ManageTokens.ScreenOpened())
addBackPressHandler(this)
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
index c7cffcb201..ab67ba0579 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt
@@ -7,6 +7,7 @@ import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.AddressType
+import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.domain.common.CardDTO
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.NotificationAction
@@ -102,6 +103,7 @@ sealed class WalletAction : Action {
data class SetPrimaryToken(val token: Token) : MultiWallet()
data class ShowWalletBackupWarning(val show: Boolean) : MultiWallet()
object BackupWallet : MultiWallet()
+ object ScheduleCheckForMissingDerivation : MultiWallet()
data class AddMissingDerivations(val blockchains: List) : MultiWallet()
object ScanToGetDerivations : MultiWallet()
}
@@ -144,7 +146,7 @@ sealed class WalletAction : Action {
object Failure : WalletAction()
}
- object Scan : WalletAction()
+ data class Scan(val onScanSuccessEvent: AnalyticsEvent?) : WalletAction()
data class Send(val amount: Amount? = null) : WalletAction()
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
index 7ace673faa..681b095f81 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt
@@ -29,6 +29,9 @@ import org.rekotlin.StateType
import java.math.BigDecimal
import kotlin.properties.ReadOnlyProperty
+/**
+ * @property derivationsCheckIsScheduled - used only for analytics
+ */
data class WalletState(
val cardId: String = "",
val state: ProgressState = ProgressState.Done,
@@ -46,6 +49,7 @@ data class WalletState(
val totalBalance: TotalBalance? = null,
val showBackupWarning: Boolean = false,
val missingDerivations: List = emptyList(),
+ val derivationsCheckIsScheduled: Boolean = false,
val loadingUserTokens: Boolean = false,
val walletCardsCount: Int? = null,
) : StateType {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt
index 78fbe2eae1..34ad6fc335 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/Mapper.kt
@@ -1,6 +1,6 @@
package com.tangem.tap.features.wallet.redux.middlewares
-import com.tangem.blockchain.common.AmountType
+import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.isZero
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.extensions.toFiatRateString
@@ -10,7 +10,6 @@ import com.tangem.tap.common.extensions.toFormattedFiatValue
import com.tangem.tap.domain.model.TotalFiatBalance
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
-import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.TotalBalance
import com.tangem.tap.features.wallet.models.WalletRent
import com.tangem.tap.features.wallet.redux.ProgressState
@@ -24,11 +23,9 @@ import com.tangem.tap.features.wallet.ui.TokenData
import com.tangem.tap.store
import java.math.BigDecimal
-internal fun List.mapToReduxModels(
- isMultiWalletAllowed: Boolean,
-): List {
+internal fun List.mapToReduxModels(): List {
return this.map { walletStoreModel ->
- walletStoreModel.mapToReduxModel(isMultiWalletAllowed)
+ walletStoreModel.mapToReduxModel()
}
}
@@ -44,103 +41,151 @@ internal fun TotalFiatBalance.mapToReduxModel(): TotalBalance {
)
}
-internal fun WalletStoreModel.mapToReduxModel(
- isMultiWalletAllowed: Boolean,
-): WalletStore {
+internal fun WalletStoreModel.mapToReduxModel(): WalletStore {
+ val appCurrencySymbol = store.state.globalState.appCurrency.symbol
return WalletStore(
walletManager = walletManager,
blockchainNetwork = blockchainNetwork,
- walletsData = walletsData.mapToReduxModel(isMultiWalletAllowed, walletRent),
+ walletsData = walletsData.mapToReduxModels(walletRent, appCurrencySymbol),
)
+ .updateTokenModels(blockchainWalletData.status.amount)
+ .setupIfHadCardSingleToken(
+ blockchain = blockchain,
+ walletsDataModel = walletsData,
+ appCurrencySymbol = appCurrencySymbol,
+ blockchainWalletData = blockchainWalletData.mapToReduxModel(
+ walletRent = walletRent,
+ appCurrencySymbol = appCurrencySymbol,
+ ),
+ )
}
@Suppress("LongMethod", "ComplexMethod")
-private fun List.mapToReduxModel(
- isMultiWalletAllowed: Boolean,
+private fun List.mapToReduxModels(
walletRent: WalletStoreModel.WalletRent?,
+ appCurrencySymbol: String,
): List {
return this.map { walletDataModel ->
- with(walletDataModel) {
- val amount = status.amount
- val amountFormatted = amount.toFormattedCurrencyString(
- decimals = currency.decimals,
- currency = currency.currencySymbol,
- )
- val appCurrency = store.state.globalState.appCurrency
- val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
- val fiatAmountFormatted = fiatAmount
- ?.takeIf { !status.isErrorStatus }
- ?.toFormattedFiatValue(appCurrency.symbol)
- val fiatRateFormatted = fiatRate?.toFiatRateString(appCurrency.symbol)
- val blockchainAmountValue = getBlockchainAmount()
-
- WalletData(
- currency = currency,
- walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
- WalletAddresses(
- selectedAddress = selectedAddress,
- list = walletAddresses,
- )
- },
- existentialDepositString = existentialDeposit?.toPlainString(),
- fiatRate = fiatRate,
- fiatRateString = fiatRateFormatted,
- pendingTransactions = status.pendingTransactions,
- mainButton = WalletMainButton.SendButton(
- enabled = !blockchainAmountValue.isZero() &&
- !status.amount.isZero() &&
- status.pendingTransactions.isEmpty(),
- ),
- walletRent = walletRent?.let {
- WalletRent(
- minRentValue = "${it.rent.stripZeroPlainString()} ${currency.blockchain.currency}",
- rentExemptValue = "${it.exemptionAmount.stripZeroPlainString()} " +
- currency.blockchain.currency,
- )
- },
- currencyData = BalanceWidgetData(
- status = when (status) {
- is WalletDataModel.Loading -> BalanceStatus.Loading
- is WalletDataModel.NoAccount -> BalanceStatus.NoAccount
- is WalletDataModel.SameCurrencyTransactionInProgress ->
- BalanceStatus.SameCurrencyTransactionInProgress
- is WalletDataModel.TransactionInProgress -> BalanceStatus.TransactionInProgress
- is WalletDataModel.Unreachable -> BalanceStatus.Unreachable
- is WalletDataModel.MissedDerivation -> BalanceStatus.MissedDerivation
- is WalletDataModel.VerifiedOnline -> BalanceStatus.VerifiedOnline
- },
- currency = currency.currencyName,
- currencySymbol = currency.currencySymbol,
- blockchainAmount = blockchainAmountValue,
- amount = amount,
- amountFormatted = amountFormatted,
- fiatAmount = fiatAmount,
- fiatAmountFormatted = fiatAmountFormatted,
- token = when {
- !isMultiWalletAllowed && currency is Currency.Token -> {
- TokenData(
- amount = amount,
- amountFormatted = amountFormatted,
- fiatAmount = fiatAmount,
- fiatAmountFormatted = fiatAmountFormatted,
- tokenSymbol = currency.currencySymbol,
- fiatRate = fiatRate,
- fiatRateString = fiatRateFormatted,
- )
- }
- else -> null
- },
- amountToCreateAccount = (status as? WalletDataModel.NoAccount)
- ?.amountToCreateAccount
- ?.toString(),
- errorMessage = status.errorMessage,
- ),
- )
- }
+ walletDataModel.mapToReduxModel(walletRent, appCurrencySymbol)
}
}
-private fun WalletDataModel.getBlockchainAmount(): BigDecimal {
- val walletStore = store.state.walletState.getWalletStore(currency) ?: return BigDecimal.ZERO
- return walletStore.walletManager?.wallet?.amounts?.get(AmountType.Coin)?.value ?: BigDecimal.ZERO
+private fun WalletDataModel.mapToReduxModel(
+ walletRent: WalletStoreModel.WalletRent?,
+ appCurrencySymbol: String,
+): WalletData {
+ val amount = status.amount
+ val amountFormatted = amount.toFormattedCurrencyString(
+ decimals = currency.decimals,
+ currency = currency.currencySymbol,
+ )
+ val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
+ val fiatAmountFormatted = fiatAmount
+ ?.takeIf { !status.isErrorStatus }
+ ?.toFormattedFiatValue(appCurrencySymbol)
+ val fiatRateFormatted = fiatRate?.toFiatRateString(appCurrencySymbol)
+
+ return WalletData(
+ currency = currency,
+ walletAddresses = walletAddresses.getOrNull(0)?.let { selectedAddress ->
+ WalletAddresses(
+ selectedAddress = selectedAddress,
+ list = walletAddresses,
+ )
+ },
+ existentialDepositString = existentialDeposit?.toPlainString(),
+ fiatRate = fiatRate,
+ fiatRateString = fiatRateFormatted,
+ pendingTransactions = status.pendingTransactions,
+ mainButton = WalletMainButton.SendButton(
+ enabled = !status.amount.isZero() && status.pendingTransactions.isEmpty(),
+ ),
+ walletRent = walletRent?.let {
+ WalletRent(
+ minRentValue = "${it.rent.stripZeroPlainString()} ${currency.blockchain.currency}",
+ rentExemptValue = "${it.exemptionAmount.stripZeroPlainString()} ${currency.blockchain.currency}",
+ )
+ },
+ currencyData = BalanceWidgetData(
+ status = when (status) {
+ is WalletDataModel.Loading -> BalanceStatus.Loading
+ is WalletDataModel.NoAccount -> BalanceStatus.NoAccount
+ is WalletDataModel.SameCurrencyTransactionInProgress -> BalanceStatus.SameCurrencyTransactionInProgress
+ is WalletDataModel.TransactionInProgress -> BalanceStatus.TransactionInProgress
+ is WalletDataModel.Unreachable -> BalanceStatus.Unreachable
+ is WalletDataModel.MissedDerivation -> BalanceStatus.MissedDerivation
+ is WalletDataModel.VerifiedOnline -> BalanceStatus.VerifiedOnline
+ },
+ currency = currency.currencyName,
+ currencySymbol = currency.currencySymbol,
+ blockchainAmount = BigDecimal.ZERO,
+ amount = amount,
+ amountFormatted = amountFormatted,
+ fiatAmount = fiatAmount,
+ fiatAmountFormatted = fiatAmountFormatted,
+ token = null,
+ amountToCreateAccount = (status as? WalletDataModel.NoAccount)
+ ?.amountToCreateAccount
+ ?.toString(),
+ errorMessage = status.errorMessage,
+ ),
+ )
+}
+
+private fun WalletStore.updateTokenModels(blockchainAmount: BigDecimal): WalletStore {
+ val updatedTokensWalletData = walletsData.filter { it.currency.isToken() }.map {
+ it.copy(
+ mainButton = when (it.mainButton) {
+ is WalletMainButton.SendButton -> {
+ WalletMainButton.SendButton(it.mainButton.enabled && !blockchainAmount.isZero())
+ }
+ is WalletMainButton.CreateWalletButton -> it.mainButton
+ },
+ currencyData = it.currencyData.copy(
+ blockchainAmount = blockchainAmount,
+ ),
+ )
+ }
+ return updateWallets(updatedTokensWalletData)
+}
+
+private fun WalletStore.setupIfHadCardSingleToken(
+ blockchain: Blockchain,
+ walletsDataModel: List,
+ appCurrencySymbol: String,
+ blockchainWalletData: WalletData,
+): WalletStore {
+ // Card with single token contains only 2 model - blockchain and token
+ if (walletsData.size != 2) return this
+
+ val cardSingleTokenWalletData = walletsDataModel.firstOrNull {
+ it.currency.isToken() && it.currency.blockchain == blockchain && it.isCardSingleToken
+ } ?: return this
+
+ val blockchainWalletDataWithSingleToken = blockchainWalletData.copy(
+ currencyData = blockchainWalletData.currencyData.copy(
+ token = cardSingleTokenWalletData.toTokenData(appCurrencySymbol),
+ ),
+ )
+ return updateWallets(listOf(blockchainWalletDataWithSingleToken))
+}
+
+private fun WalletDataModel.toTokenData(appCurrencySymbol: String): TokenData {
+ val amount = status.amount
+ val fiatAmount = fiatRate?.let { status.amount.toFiatValue(it) }
+
+ return TokenData(
+ amount = amount,
+ amountFormatted = amount.toFormattedCurrencyString(
+ decimals = currency.decimals,
+ currency = currency.currencySymbol,
+ ),
+ fiatAmount = fiatAmount,
+ fiatAmountFormatted = fiatAmount
+ ?.takeIf { !status.isErrorStatus }
+ ?.toFormattedFiatValue(appCurrencySymbol),
+ tokenSymbol = currency.currencySymbol,
+ fiatRate = fiatRate,
+ fiatRateString = fiatRate?.toFiatRateString(appCurrencySymbol),
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
index b3957806f4..1f322a6152 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt
@@ -8,7 +8,6 @@ import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.events.AnalyticsParam
-import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
@@ -174,12 +173,15 @@ class MultiWalletMiddleware {
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
}
}
+ is WalletAction.MultiWallet.AddMissingDerivations -> {
+ scope.launch { handleBasicAnalyticsEvent() }
+ }
is WalletAction.MultiWallet.ScanToGetDerivations -> {
val selectedWallet = userWalletsListManager.selectedUserWalletSync
if (selectedWallet != null) {
scanAndUpdateCard(selectedWallet, walletState)
} else {
- store.dispatch(WalletAction.Scan)
+ store.dispatch(WalletAction.Scan(onScanSuccessEvent = null))
}
}
else -> {}
@@ -190,8 +192,9 @@ class MultiWalletMiddleware {
selectedUserWallet: UserWallet,
state: WalletState?,
) = scope.launch(Dispatchers.Default) {
- Analytics.send(MainScreen.CardWasScanned())
+ dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
ScanCardProcessor.scan(
+ analyticsEvent = null,
cardId = selectedUserWallet.cardId,
additionalBlockchainsToDerive = state?.missingDerivations?.map { it.blockchain },
) { scanResponse ->
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 ee4f964106..1afc0422ec 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
@@ -48,21 +48,22 @@ class TradeCryptoMiddleware {
state: () -> AppState?,
action: WalletAction.TradeCryptoAction.Buy,
) {
+ val selectedWalletData = store.state.walletState.selectedWalletData ?: return
+
+ val currency = selectedWalletData.currency
+ Analytics.send(Token.ButtonBuy(AnalyticsParam.CurrencyType.Currency(currency)))
+
if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) {
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog())
return
}
- val selectedWalletData = store.state.walletState.selectedWalletData ?: return
val card = store.state.globalState.scanResponse?.card ?: return
-
val addresses = selectedWalletData.walletAddresses?.list.orEmpty()
if (addresses.isEmpty()) return
val exchangeManager = store.state.globalState.exchangeManager
val appCurrency = store.state.globalState.appCurrency
- val currency = selectedWalletData.currency
- Analytics.send(Token.ButtonBuy(AnalyticsParam.CurrencyType.Currency(currency)))
if (currency is Currency.Token && currency.blockchain.isTestnet()) {
val walletManager = store.state.walletState.getWalletManager(currency)
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
index c47055fc46..437c1fd905 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt
@@ -12,6 +12,9 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.attestation.Attestation
import com.tangem.operations.attestation.OnlineCardVerifier
+import com.tangem.tap.common.analytics.converters.BasicEventsPreChecker
+import com.tangem.tap.common.analytics.converters.BasicEventsSourceData
+import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.copyToClipboard
@@ -33,6 +36,7 @@ import com.tangem.tap.domain.failedRates
import com.tangem.tap.domain.loadedRates
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
+import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
@@ -54,10 +58,12 @@ import com.tangem.tap.tangemSdkManager
import com.tangem.tap.totalFiatBalanceCalculator
import com.tangem.tap.userWalletsListManager
import com.tangem.tap.userWalletsListManagerSafe
+import com.tangem.tap.walletStoresManager
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
@@ -109,6 +115,7 @@ class WalletMiddleware {
walletState.walletManagers.map { walletManager ->
async { globalState.tapWalletManager.loadWalletData(walletManager) }
}.awaitAll()
+ handleBasicAnalyticsEvent()
} else {
val walletManager = walletState.getWalletManager(action.blockchain)
?: action.walletManager
@@ -194,8 +201,11 @@ class WalletMiddleware {
}
}
is WalletAction.Scan -> {
- store.dispatch(HomeAction.ShouldScanCardOnResume(true))
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
+ scope.launch {
+ delay(700)
+ store.dispatchOnMain(HomeAction.ReadCard(action.onScanSuccessEvent))
+ }
}
is WalletAction.LoadCardInfo -> {
val attestationFailed = action.card.attestation.status == Attestation.Status.Failed
@@ -249,10 +259,12 @@ class WalletMiddleware {
}
}
is WalletAction.CopyAddress -> {
+ Analytics.send(Token.Receive.ButtonCopyAddress())
action.context.copyToClipboard(action.address)
store.dispatch(WalletAction.CopyAddress.Success)
}
is WalletAction.ShareAddress -> {
+ Analytics.send(Token.Receive.ButtonShareAddress())
action.context.shareText(action.address)
}
is WalletAction.ExploreAddress -> {
@@ -274,6 +286,9 @@ class WalletMiddleware {
} else {
store.dispatch(newAction)
if (newAction is PrepareSendScreen) {
+ store.state.walletState.selectedWalletData?.currency?.let { currency ->
+ Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(currency)))
+ }
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
}
}
@@ -286,6 +301,7 @@ class WalletMiddleware {
}
is WalletAction.UserWalletChanged -> Unit
is WalletAction.WalletStoresChanged -> {
+ store.dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
updateWalletStores(action.walletStores, walletState)
fetchTotalFiatBalance(action.walletStores)
findMissedDerivations(action.walletStores)
@@ -303,7 +319,7 @@ class WalletMiddleware {
}
}
- val reduxWalletStores = wallStores.mapToReduxModels(state.isMultiwalletAllowed)
+ val reduxWalletStores = wallStores.mapToReduxModels()
store.dispatchOnMain(
WalletAction.WalletStoresChanged.UpdateWalletStores(
reduxWalletStores = reduxWalletStores,
@@ -361,7 +377,7 @@ class WalletMiddleware {
}
else -> {
Analytics.send(MainScreen.ButtonScanCard())
- store.dispatch(WalletAction.Scan)
+ store.dispatch(WalletAction.Scan(MainScreen.CardWasScanned()))
}
}
}
@@ -481,4 +497,19 @@ class WalletMiddleware {
}
}
}
+}
+
+suspend fun handleBasicAnalyticsEvent() {
+ val scanResponse = store.state.globalState.scanResponse ?: return
+
+ val biometricsWalletDataModels = if (preferencesStorage.shouldSaveUserWallets) {
+ UserWalletIdBuilder.scanResponse(scanResponse).build()?.let { userWalletId ->
+ walletStoresManager.getSync(userWalletId).map { it.walletsData }.flatten()
+ }
+ } else {
+ null
+ }
+
+ val converterData = BasicEventsSourceData(scanResponse, store.state.walletState, biometricsWalletDataModels)
+ BasicEventsPreChecker().tryToSend(converterData)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt
index d8ffb0df63..c772684c48 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt
@@ -1,7 +1,6 @@
package com.tangem.tap.features.wallet.redux.models
import com.tangem.blockchain.common.Amount
-import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.StateDialog
import com.tangem.wallet.R
@@ -38,6 +37,6 @@ sealed interface WalletDialog : StateDialog {
}
data class RussianCardholdersWarningDialog(val data: Data?) : WalletDialog {
- data class Data(val topUpUrl: String, val blockchain: Blockchain)
+ data class Data(val topUpUrl: String)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
index 86a92f276a..3553809ffb 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt
@@ -187,7 +187,13 @@ class MultiWalletReducer {
is WalletAction.MultiWallet.SetPrimaryToken -> state.copy(primaryToken = action.token)
is WalletAction.MultiWallet.SaveCurrencies -> state
is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy(showBackupWarning = action.show)
- is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(missingDerivations = action.blockchains)
+ is WalletAction.MultiWallet.ScheduleCheckForMissingDerivation -> state.copy(
+ derivationsCheckIsScheduled = true,
+ )
+ is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(
+ missingDerivations = action.blockchains,
+ derivationsCheckIsScheduled = false
+ )
is WalletAction.MultiWallet.BackupWallet -> state
is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(state = ProgressState.Loading)
}
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
index fdc0f2fd60..6d9c01d27c 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt
@@ -16,6 +16,8 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
+import com.badoo.mvicore.DiffStrategy
+import com.badoo.mvicore.ModelWatcher
import com.badoo.mvicore.modelWatcher
import com.tangem.common.doOnResult
import com.tangem.core.analytics.Analytics
@@ -39,6 +41,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction
+import com.tangem.tap.features.wallet.models.WalletWarning
+import com.tangem.tap.features.wallet.redux.AddressData
import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
@@ -68,7 +72,13 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind)
- private val walletDataWatcher = modelWatcher {
+ private val walletDataWatcher: ModelWatcher = modelWatcher {
+ val addressCardStrategy: DiffStrategy = { old, new ->
+ old.currency != new.currency ||
+ old.walletAddresses?.selectedAddress != new.walletAddresses?.selectedAddress ||
+ old.shouldShowMultipleAddress() != new.shouldShowMultipleAddress()
+ }
+
WalletData::pendingTransactions {
showPendingTransactionsIfPresent(it)
}
@@ -78,25 +88,35 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
WalletData::currencyData {
setupBalanceData(it)
}
+ WalletData::walletAddresses { walletAddresses ->
+ setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address)
+ }
+ WalletData::assembleWarnings { warnings ->
+ handleWarnings(warnings)
+ }
(WalletData::currencyData or WalletData::currency) { walletData ->
setupCurrency(walletData.currencyData, walletData.currency)
setupSwipeRefresh(walletData.currencyData, walletData.currency)
}
+ watch({ it }, addressCardStrategy) { walletData ->
+ setupAddressCard(
+ shouldShowMultipleAddress = walletData.shouldShowMultipleAddress(),
+ selectedAddress = walletData.walletAddresses?.selectedAddress,
+ currency = walletData.currency,
+ )
+ }
}
- private val walletStateWatcher = modelWatcher {
- (WalletState::selectedCurrency or WalletState::selectedWalletData) { state ->
- val selectedWalletData = state.selectedWalletData
- if (selectedWalletData != null) {
- walletDataWatcher.invoke(selectedWalletData)
- setupButtons(selectedWalletData, state.isExchangeServiceFeatureOn)
- setupAddressCard(selectedWalletData)
- handleWarnings(selectedWalletData)
+ private val walletStateWatcher: ModelWatcher = modelWatcher {
+ WalletState::selectedWalletData { selectedWallet ->
+ if (selectedWallet != null) {
+ walletDataWatcher.invoke(selectedWallet)
}
}
- (WalletState::selectedCurrency or WalletState::isExchangeServiceFeatureOn) { state ->
- if (state.selectedWalletData != null) {
- setupButtons(state.selectedWalletData!!, state.isExchangeServiceFeatureOn)
+ (WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state ->
+ val selectedWallet = state.selectedWalletData
+ if (selectedWallet != null) {
+ setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn)
}
}
(WalletState::state or WalletState::error) { state ->
@@ -133,8 +153,6 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
override fun onStop() {
super.onStop()
store.unsubscribe(this)
- walletDataWatcher.clear()
- walletStateWatcher.clear()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -148,6 +166,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
setupTestActionButton()
}
+ override fun onDestroyView() {
+ super.onDestroyView()
+ clearWatchers()
+ }
+
private fun setupTransactionsRecyclerView() = with(binding) {
pendingTransactionAdapter = PendingTransactionsAdapter()
rvPendingTransaction.layoutManager = LinearLayoutManager(requireContext())
@@ -243,23 +266,25 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
currencyData.status == BalanceStatus.Refreshing
}
- private fun setupButtons(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) = with(binding) {
- lWalletDetails.btnCopy.setOnClickListener {
- selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
- store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
+ private fun setupCopyAndShareButtons(walletAddress: String?) {
+ binding.lWalletDetails.btnCopy.setOnClickListener {
+ if (walletAddress != null) {
+ store.dispatch(WalletAction.CopyAddress(walletAddress, requireContext()))
}
}
- lWalletDetails.btnShare.setOnClickListener {
- selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
- store.dispatch(WalletAction.ShareAddress(addressString, requireContext()))
+ binding.lWalletDetails.btnShare.setOnClickListener {
+ if (walletAddress != null) {
+ store.dispatch(WalletAction.ShareAddress(walletAddress, requireContext()))
}
}
+ }
- rowButtons.updateButtonsVisibility(
+ private fun setupButtonsRow(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) {
+ binding.rowButtons.updateButtonsVisibility(
exchangeServiceFeatureOn = isExchangeServiceFeatureOn,
sendAllowed = selectedWallet.mainButton.enabled,
)
- rowButtons.onTradeClick = {
+ binding.rowButtons.onTradeClick = {
store.dispatch(
WalletAction.DialogAction.ChooseTradeActionDialog(
buyAllowed = selectedWallet.isAvailableToBuy,
@@ -270,9 +295,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
}
}
- private fun handleWarnings(selectedWallet: WalletData) = with(binding) {
+ private fun handleWarnings(warnings: List) = with(binding) {
val converter = WalletWarningConverter(requireContext())
- val warningDetails = selectedWallet.assembleWarnings().map { converter.convert(it) }
+ val warningDetails = warnings.map { converter.convert(it) }
warningMessagesAdapter.submitList(warningDetails)
rvWarningMessages.show(warningDetails.isNotEmpty())
@@ -293,51 +318,59 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
binding.rvPendingTransaction.show(pendingTransactions.isNotEmpty())
}
- private fun setupAddressCard(state: WalletData) = with(binding.lWalletDetails) {
- if (state.walletAddresses != null) {
- if (state.shouldShowMultipleAddress() && state.currency is Currency.Blockchain) {
- (cardBalance as? ViewGroup)?.beginDelayedTransition()
- chipGroupAddressType.show()
- chipGroupAddressType.fitChipsByGroupWidth()
+ private fun setupAddressCard(
+ shouldShowMultipleAddress: Boolean,
+ selectedAddress: AddressData?,
+ currency: Currency,
+ ) = with(binding.lWalletDetails) {
+ if (selectedAddress == null) return@with
- val checkedId =
- MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
- if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
+ setupAddressTypeChips(shouldShowMultipleAddress, selectedAddress, currency)
- chipGroupAddressType.setOnCheckedChangeListener { group, checkedId ->
- if (checkedId == -1) return@setOnCheckedChangeListener
- val type =
- MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain)
- type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
- }
- } else {
- chipGroupAddressType.hide()
- }
- tvAddress.text = state.walletAddresses.selectedAddress.address
- tvExplore.setOnClickListener {
- store.dispatch(
- WalletAction.ExploreAddress(
- state.walletAddresses.selectedAddress.exploreUrl,
- requireContext(),
- ),
- )
- }
- ivQrCode.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode())
+ tvAddress.text = selectedAddress.address
+ tvExplore.setOnClickListener {
+ store.dispatch(WalletAction.ExploreAddress(selectedAddress.exploreUrl, requireContext()))
+ }
+ ivQrCode.setImageBitmap(selectedAddress.shareUrl.toQrCode())
- tvReceiveMessage.text = when (val currency = state.currency) {
- is Currency.Blockchain -> tvReceiveMessage.getString(
- id = R.string.address_qr_code_message_format,
- currency.blockchain.fullName,
- currency.currencySymbol,
- currency.blockchain.fullName,
- )
- is Currency.Token -> tvReceiveMessage.getString(
- id = R.string.address_qr_code_message_format,
- currency.token.name,
- currency.currencySymbol,
- currency.blockchain.fullName,
- )
+ tvReceiveMessage.text = when (currency) {
+ is Currency.Blockchain -> tvReceiveMessage.getString(
+ id = R.string.address_qr_code_message_format,
+ currency.blockchain.fullName,
+ currency.currencySymbol,
+ currency.blockchain.fullName,
+ )
+ is Currency.Token -> tvReceiveMessage.getString(
+ id = R.string.address_qr_code_message_format,
+ currency.token.name,
+ currency.currencySymbol,
+ currency.blockchain.fullName,
+ )
+ }
+ }
+
+ private fun setupAddressTypeChips(
+ shouldShowMultipleAddress: Boolean,
+ selectedAddress: AddressData,
+ currency: Currency,
+ ) = with(binding.lWalletDetails) {
+ if (shouldShowMultipleAddress && currency is Currency.Blockchain) {
+ (cardBalance as? ViewGroup)?.beginDelayedTransition()
+ chipGroupAddressType.show()
+ chipGroupAddressType.fitChipsByGroupWidth()
+
+ val checkedId =
+ MultipleAddressUiHelper.typeToId(selectedAddress.type)
+ if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
+
+ chipGroupAddressType.setOnCheckedChangeListener { _, checkedId ->
+ if (checkedId == -1) return@setOnCheckedChangeListener
+ val type =
+ MultipleAddressUiHelper.idToType(checkedId, currency.blockchain)
+ type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
}
+ } else {
+ chipGroupAddressType.hide()
}
}
@@ -426,6 +459,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
inflater.inflate(R.menu.menu_wallet_details, menu)
}
+ private fun clearWatchers() {
+ walletDataWatcher.clear()
+ walletStateWatcher.clear()
+ }
+
private fun TextView.setWarningStatus(mainMessage: Int, error: String? = null) {
val text = getString(mainMessage).appendIfNotNull(error, "\nError: ")
setStatus(text, R.color.warning, R.drawable.ic_warning_small)
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
index 964cafee2b..df88f6b063 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt
@@ -22,8 +22,6 @@ import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.core.ui.utils.OneTouchClickListener
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.tap.MainActivity
-import com.tangem.tap.common.analytics.converters.BasicSignInEventConverter
-import com.tangem.tap.common.analytics.converters.BasicTopUpEventConverter
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.show
@@ -150,7 +148,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber {
context.getString(
R.string.token_details_send_blocked_tx_format,
- message.currencyName
+ message.currencyName,
)
}
is WalletWarning.Rent -> {
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt
index 29bef197e1..667d056d40 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt
@@ -41,8 +41,6 @@ class RussianCardholdersWarningBottomSheetDialog(
binding?.btnYes?.setOnClickListener {
if (dialogData != null) {
- val currencyType = AnalyticsParam.CurrencyType.Blockchain(dialogData.blockchain)
- Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType))
store.dispatchOpenUrl(dialogData.topUpUrl)
} else {
store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false))
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
index 3a66140412..00bd560d80 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt
@@ -9,6 +9,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.tap.common.analytics.events.MainScreen
+import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.animateVisibility
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
@@ -136,6 +137,8 @@ class MultiWalletView : WalletView() {
derivationStyle = card.derivationStyle,
),
)
+
+ Analytics.send(ManageTokens.ScreenOpened())
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
}
handleErrorStates(state = state, binding = binding, fragment = fragment)
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
index 6b53c12857..9390d7601a 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt
@@ -12,6 +12,7 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.models.PendingTransaction
+import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletMainButton
@@ -72,8 +73,11 @@ class SingleWalletView : WalletView() {
}
private fun showPendingTransactionsIfPresent(pendingTransactions: List) {
- pendingTransactionAdapter.submitList(pendingTransactions)
- binding?.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
+ val knownTransactions = pendingTransactions.filterNot {
+ it.type == PendingTransactionType.Unknown
+ }
+ pendingTransactionAdapter.submitList(knownTransactions)
+ binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty())
}
private fun setupBalance(state: WalletState, primaryWallet: WalletData) {
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt
index 17824db23f..4e8b5ec2d6 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt
@@ -24,8 +24,8 @@ internal sealed interface WalletSelectorAction : Action {
val walletsStores: Map>,
) : WalletSelectorAction
- data class BalanceLoaded(
- val userWalletModel: UserWalletModel,
+ data class BalancesLoaded(
+ val userWalletModels: List,
) : WalletSelectorAction
object UnlockWithBiometry : WalletSelectorAction {
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt
index 96896eb57a..4debaff531 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt
@@ -10,6 +10,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.analytics.events.MyWallets
+import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
@@ -21,6 +22,7 @@ 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.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
@@ -35,7 +37,6 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import org.rekotlin.Middleware
import timber.log.Timber
-import java.math.BigDecimal
// Refactoring is coming
@Suppress("LargeClass")
@@ -83,7 +84,7 @@ internal class WalletSelectorMiddleware {
is WalletSelectorAction.SelectedWalletChanged,
is WalletSelectorAction.UnlockWithBiometry.Error,
is WalletSelectorAction.UnlockWithBiometry.Success,
- is WalletSelectorAction.BalanceLoaded,
+ is WalletSelectorAction.BalancesLoaded,
is WalletSelectorAction.IsLockedChanged,
is WalletSelectorAction.CloseError,
-> Unit
@@ -103,28 +104,16 @@ internal class WalletSelectorMiddleware {
updatedWalletStores: Map>,
state: WalletSelectorState,
) {
- if (updatedWalletStores.isNotEmpty()) {
- scope.launch(Dispatchers.Default) {
- state.wallets
- .associateWith { updatedWalletStores[it.id] }
- .forEach { (wallet, walletStores) ->
- val isWalletTokensEmpty = (wallet.type as? UserWalletModel.Type.MultiCurrency)?.tokensCount == 0
- val updatedWallet = if (walletStores == null && isWalletTokensEmpty) {
- wallet.copy(fiatBalance = TotalFiatBalance.Loaded(BigDecimal.ZERO))
- } else {
- wallet.updateWalletStoresAndCalculateFiatBalance(walletStores.orEmpty())
- }
-
- if (wallet != updatedWallet) {
- store.dispatchOnMain(WalletSelectorAction.BalanceLoaded(updatedWallet))
- }
- }
+ if (updatedWalletStores.isNotEmpty()) scope.launch(Dispatchers.Default) {
+ val updatedWallets = state.wallets.updateWalletStoresAndCalculateFiatBalance(updatedWalletStores)
+ if (updatedWallets != state.wallets) {
+ store.dispatchOnMain(WalletSelectorAction.BalancesLoaded(updatedWallets))
}
}
}
private fun unlockWalletsWithBiometry() {
- Analytics.send(MyWallets.Button.UnlockWithBiometrics)
+ Analytics.send(MyWallets.Button.UnlockWithBiometrics())
scope.launch {
userWalletsListManager.unlockWithBiometry()
@@ -139,7 +128,7 @@ internal class WalletSelectorMiddleware {
}
private fun addWallet() = scope.launch {
- Analytics.send(MyWallets.Button.ScanNewCard)
+ Analytics.send(MyWallets.Button.ScanNewCard())
val prevUseBiometricsForAccessCode = tangemSdkManager.useBiometricsForAccessCode()
@@ -149,6 +138,7 @@ internal class WalletSelectorMiddleware {
)
ScanCardProcessor.scan(
+ analyticsEvent = MyWallets.CardWasScanned(),
onWalletNotCreated = {
// No need to rollback policy, continue with the policy set before the card scan
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
@@ -169,8 +159,16 @@ internal class WalletSelectorMiddleware {
onFailure = { error ->
// Rollback policy if card scanning was failed
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
- Timber.e(error, "Unable to scan card")
- store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
+ when {
+ error is TangemSdkError.ExceptionError && error.cause is SaltPayActivationError -> {
+ store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
+ store.dispatchOnMain(NavigationAction.PopBackTo())
+ }
+ else -> {
+ Timber.e(error, "Unable to scan card")
+ store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
+ }
+ }
},
)
}
@@ -181,8 +179,6 @@ internal class WalletSelectorMiddleware {
return userWalletsListManager.save(userWallet)
.doOnSuccess {
- Analytics.send(MyWallets.CardWasScanned)
-
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
store.onUserWalletSelected(userWallet)
@@ -211,6 +207,9 @@ internal class WalletSelectorMiddleware {
.doOnSuccess {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
+ val batchId = selectedUserWallet.scanResponse.card.batchId
+ Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId))
+
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
store.onUserWalletSelected(selectedUserWallet)
}
@@ -219,6 +218,7 @@ internal class WalletSelectorMiddleware {
}
private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult {
+ Analytics.send(MyWallets.Button.WalletUnlockTapped())
tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse)
return tangemSdkManager.scanProduct(userTokensRepository)
.map { scanResponse ->
@@ -258,7 +258,7 @@ internal class WalletSelectorMiddleware {
}
private fun deleteWallets(userWalletsIds: List, state: WalletSelectorState) {
- Analytics.send(MyWallets.Button.DeleteWalletTapped)
+ Analytics.send(MyWallets.Button.DeleteWalletTapped())
scope.launch {
when (userWalletsIds.size) {
@@ -282,8 +282,6 @@ internal class WalletSelectorMiddleware {
}
private fun renameWallet(userWalletId: UserWalletId, newName: String) {
- Analytics.send(MyWallets.Button.EditWalletTapped)
-
scope.launch {
userWalletsListManager.update(userWalletId) { it.copy(name = newName) }
.doOnFailure { error ->
@@ -357,18 +355,28 @@ internal class WalletSelectorMiddleware {
}
}
+ private suspend fun List.updateWalletStoresAndCalculateFiatBalance(
+ walletStores: Map>,
+ ): List {
+ return this
+ .associateWith { walletStores[it.id] }
+ .map { (wallet, walletStores) ->
+ wallet.updateWalletStoresAndCalculateFiatBalance(walletStores)
+ }
+ }
+
private suspend fun UserWalletModel.updateWalletStoresAndCalculateFiatBalance(
- walletStores: List,
+ walletStores: List?,
): UserWalletModel {
return this.copy(
type = when (type) {
is UserWalletModel.Type.MultiCurrency -> type.copy(
- tokensCount = walletStores.flatMap { it.walletsData }.size,
+ tokensCount = walletStores?.flatMap { it.walletsData }?.size ?: 0,
)
is UserWalletModel.Type.SingleCurrency -> type
},
fiatBalance = totalFiatBalanceCalculator.calculate(
- walletStores = walletStores,
+ walletStores = walletStores.orEmpty(),
initial = TotalFiatBalance.Loading,
),
)
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt
index 5c6823a485..e965a29794 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt
@@ -25,8 +25,8 @@ internal object WalletSelectorReducer {
is WalletSelectorAction.IsLockedChanged -> state.copy(
isLocked = action.isLocked,
)
- is WalletSelectorAction.BalanceLoaded -> state.copy(
- wallets = state.wallets.updateWithBalance(action.userWalletModel),
+ is WalletSelectorAction.BalancesLoaded -> state.copy(
+ wallets = action.userWalletModels,
)
is WalletSelectorAction.CloseError -> state.copy(error = null)
is WalletSelectorAction.UnlockWithBiometry -> state.copy(
@@ -85,19 +85,6 @@ internal object WalletSelectorReducer {
}
}
- private fun List.updateWithBalance(
- userWalletModel: UserWalletModel,
- ): List {
- return ArrayList(this).apply {
- val index = indexOfFirst { it.id == userWalletModel.id }
- if (index == -1) {
- add(userWalletModel)
- } else {
- this[index] = userWalletModel
- }
- }
- }
-
private fun UserWallet.getType(prevType: UserWalletModel.Type? = null): UserWalletModel.Type {
return if (isMultiCurrency) {
UserWalletModel.Type.MultiCurrency(
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt
index e5dc088a55..5d18c5baa9 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt
@@ -9,6 +9,7 @@ import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem
import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem
import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
+import java.math.BigDecimal
internal fun WalletSelectorScreenState.updateWithNewState(
newState: WalletSelectorState,
@@ -50,10 +51,14 @@ private fun List.toUiModels(
): Sequence {
return this.asSequence().map { userWalletModel ->
with(userWalletModel) {
- val balance = UserWalletItem.Balance(
- amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol),
- isLoading = fiatBalance is TotalFiatBalance.Loading,
- )
+ val formatAmount = { amount: BigDecimal ->
+ amount.toFormattedFiatValue(appCurrency.symbol)
+ }
+ val balance = when (fiatBalance) {
+ is TotalFiatBalance.Error -> UserWalletItem.Balance.Error(formatAmount(fiatBalance.amount))
+ is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded(formatAmount(fiatBalance.amount))
+ is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading
+ }
when (type) {
is UserWalletModel.Type.MultiCurrency -> MultiCurrencyUserWalletItem(
id = id,
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt
index 9ccbecd997..4e0edeb703 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt
@@ -33,7 +33,7 @@ internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- Analytics.send(MyWallets.MyWalletsScreenOpened)
+ Analytics.send(MyWallets.MyWalletsScreenOpened())
return super.onCreateDialog(savedInstanceState)
}
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt
index 52a99013e3..edb923f5ba 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt
@@ -2,7 +2,9 @@ package com.tangem.tap.features.walletSelector.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.util.UserWalletId
+import com.tangem.tap.common.analytics.events.MyWallets
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
import com.tangem.tap.features.walletSelector.redux.WalletSelectorState
@@ -71,6 +73,7 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt
index 65349386dc..db09868d03 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt
@@ -9,9 +9,8 @@ import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem
internal object MockData {
private val multiCurrencyUserWallet = MultiCurrencyUserWalletItem(
id = UserWalletId("wallet_1"),
- balance = UserWalletItem.Balance(
+ balance = UserWalletItem.Balance.Loaded(
amount = "6781.05 $",
- isLoading = false,
),
name = "Wallet",
imageUrl = "https://app.tangem.com/cards/card_default.png",
@@ -22,9 +21,8 @@ internal object MockData {
private val singleCurrencyUserWallet = SingleCurrencyUserWalletItem(
id = UserWalletId("wallet_4"),
- balance = UserWalletItem.Balance(
+ balance = UserWalletItem.Balance.Loaded(
amount = "6781.05 $",
- isLoading = false,
),
name = "Wallet",
imageUrl = "https://app.tangem.com/cards/card_default.png",
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt
index 9bc603525d..b67acbf338 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt
@@ -171,13 +171,24 @@ private fun RowScope.TokensInfo(
if (isLocked) {
LockedPlaceholder()
} else {
- if (balance.isLoading) {
- LoadingTokensInfo(isMultiCurrencyWallet = tokensCount != null)
- } else {
- LoadedTokensInfo(
- balanceAmount = balance.amount,
- tokensCount = tokensCount,
- )
+ when (balance) {
+ is UserWalletItem.Balance.Error -> {
+ LoadedTokensInfo(
+ balanceAmount = balance.amount,
+ tokensCount = tokensCount,
+ showWarning = true,
+ )
+ }
+ is UserWalletItem.Balance.Loaded -> {
+ LoadedTokensInfo(
+ balanceAmount = balance.amount,
+ tokensCount = tokensCount,
+ showWarning = false,
+ )
+ }
+ is UserWalletItem.Balance.Loading -> {
+ LoadingTokensInfo(isMultiCurrencyWallet = tokensCount != null)
+ }
}
}
}
@@ -216,14 +227,35 @@ private fun LoadingTokensInfo(isMultiCurrencyWallet: Boolean) {
@OptIn(ExperimentalComposeUiApi::class)
@Composable
-private fun LoadedTokensInfo(balanceAmount: String, tokensCount: Int?) {
- Column(horizontalAlignment = Alignment.End) {
- Text(
- text = balanceAmount,
- style = TangemTheme.typography.subtitle1,
- color = TangemTheme.colors.text.primary1,
- textAlign = TextAlign.End,
- )
+private fun LoadedTokensInfo(
+ modifier: Modifier = Modifier,
+ balanceAmount: String,
+ tokensCount: Int?,
+ showWarning: Boolean,
+) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.End,
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
+ ) {
+ Text(
+ text = balanceAmount,
+ style = TangemTheme.typography.subtitle1,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.End,
+ )
+ if (showWarning) {
+ Icon(
+ modifier = Modifier.size(TangemTheme.dimens.size16),
+ painter = painterResource(id = R.drawable.ic_alert_24),
+ tint = TangemTheme.colors.icon.attention,
+ contentDescription = null,
+ )
+ }
+ }
if (tokensCount != null) {
SpacerH2()
Text(
diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt
index ddd7518ca6..eeca9d7611 100644
--- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt
+++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt
@@ -9,10 +9,13 @@ internal sealed interface UserWalletItem {
val balance: Balance
val isLocked: Boolean
- data class Balance(
- val amount: String,
- val isLoading: Boolean,
- )
+ sealed interface Balance {
+ object Loading : Balance
+
+ data class Error(val amount: String) : Balance
+
+ data class Loaded(val amount: String) : Balance
+ }
}
internal data class MultiCurrencyUserWalletItem(
diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt
index 726b57a823..b9e142d8ba 100644
--- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt
@@ -1,9 +1,11 @@
package com.tangem.tap.features.welcome.redux
import android.content.Intent
+import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.domain.common.ScanResponse
+import com.tangem.tap.common.analytics.events.SignIn
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
@@ -11,6 +13,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.model.builders.UserWalletBuilder
import com.tangem.tap.domain.scanCard.ScanCardProcessor
+import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
import com.tangem.tap.intentHandler
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
@@ -98,11 +101,19 @@ internal class WelcomeMiddleware {
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
+ analyticsEvent = SignIn.CardWasScanned(),
onSuccess = { scanResponse ->
scope.launch { onCardScanned(scanResponse) }
},
onFailure = {
- store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(it))
+ when {
+ it is TangemSdkError.ExceptionError && it.cause is SaltPayActivationError -> {
+ store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
+ }
+ else -> {
+ store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(it))
+ }
+ }
},
onWalletNotCreated = {
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt
index 0afd4fe8f6..7cebf15891 100644
--- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt
@@ -1,5 +1,6 @@
package com.tangem.tap.features.welcome.ui
+import android.os.Bundle
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
@@ -18,9 +19,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.fragment.app.viewModels
+import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.fragments.ComposeFragment
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.tap.common.analytics.events.SignIn
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
import com.tangem.tap.features.welcome.ui.components.WelcomeScreenContent
import com.tangem.wallet.R
@@ -28,6 +31,11 @@ import com.tangem.wallet.R
internal class WelcomeFragment : ComposeFragment() {
private val viewModel by viewModels()
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Analytics.send(SignIn.ScreenOpened())
+ }
+
@Composable
override fun provideState(): State {
return viewModel.state.collectAsState()
diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt
index a047d8518e..b810503287 100644
--- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt
+++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt
@@ -1,6 +1,8 @@
package com.tangem.tap.features.welcome.ui
import androidx.lifecycle.ViewModel
+import com.tangem.core.analytics.Analytics
+import com.tangem.tap.common.analytics.events.SignIn
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.details.ui.cardsettings.TextReference
import com.tangem.tap.features.welcome.redux.WelcomeAction
@@ -21,10 +23,12 @@ internal class WelcomeViewModel : ViewModel(), StoreSubscriber {
}
fun unlockWallets() {
+ Analytics.send(SignIn.ButtonBiometricSignIn())
store.dispatch(WelcomeAction.ProceedWithBiometrics)
}
fun scanCard() {
+ Analytics.send(SignIn.ButtonCardSignIn())
store.dispatch(WelcomeAction.ProceedWithCard)
}
diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml
index 9e8f55e880..6d8b8d8b5a 100644
--- a/app/src/main/res/layout/fragment_wallet.xml
+++ b/app/src/main/res/layout/fragment_wallet.xml
@@ -151,13 +151,12 @@
android:id="@+id/rv_pending_transaction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="12dp"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/l_card_total_balance"
- app:layout_goneMarginTop="12dp" />
+ app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
@@ -194,7 +194,7 @@
android:nestedScrollingEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
+ app:layout_constraintTop_toBottomOf="@id/l_card_total_balance" />
+ app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
- Tangem
+ Tangem
\ No newline at end of file
diff --git a/network/src/main/java/com/tangem/network/common/MoshiConverter.kt b/network/src/main/java/com/tangem/network/common/MoshiConverter.kt
new file mode 100644
index 0000000000..e69de29bb2