Updated on 2026-08-14
This commit is contained in:
commit
978bf25e96
28 changed files with 321 additions and 61 deletions
|
|
@ -60,8 +60,18 @@ sealed class AnalyticsParam {
|
|||
object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
}
|
||||
|
||||
sealed class ScannedFrom(val value: String) {
|
||||
object Introduction : ScannedFrom("Introduction")
|
||||
object Main : ScannedFrom("Main")
|
||||
object SignIn : ScannedFrom("Sign In")
|
||||
object MyWallets : ScannedFrom("My Wallets")
|
||||
}
|
||||
|
||||
companion object Key {
|
||||
const val BatchId = "Batch"
|
||||
const val Batch = "Batch"
|
||||
const val ProductType = "Product Type"
|
||||
const val Firmware = "Firmware"
|
||||
const val Currency = "Currency"
|
||||
const val ErrorDescription = "Error Description"
|
||||
const val ErrorCode = "Error Code"
|
||||
const val ErrorKey = "Error Key"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,15 @@ sealed class Basic(
|
|||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Basic", event, params, error) {
|
||||
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScannedFrom,
|
||||
) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
"Source" to source.value,
|
||||
),
|
||||
)
|
||||
|
||||
class SignedIn(
|
||||
state: AnalyticsParam.CardBalanceState,
|
||||
currency: AnalyticsParam.CardCurrency,
|
||||
|
|
@ -19,14 +28,14 @@ sealed class Basic(
|
|||
event = "Signed in",
|
||||
params = mapOf(
|
||||
"State" to state.value,
|
||||
"Currency" to currency.value,
|
||||
AnalyticsParam.BatchId to batch,
|
||||
AnalyticsParam.Currency to currency.value,
|
||||
AnalyticsParam.Batch to batch,
|
||||
),
|
||||
)
|
||||
|
||||
class ToppedUp(currency: AnalyticsParam.CardCurrency) : Basic(
|
||||
event = "Topped up",
|
||||
params = mapOf("Currency" to currency.value),
|
||||
params = mapOf(AnalyticsParam.Currency to currency.value),
|
||||
)
|
||||
|
||||
class ScanError(error: Throwable) : Basic(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,5 @@ sealed class IntroductionProcess(
|
|||
class ButtonTokensList : IntroductionProcess("Button - Tokens List")
|
||||
class ButtonBuyCards : IntroductionProcess("Button - Buy Cards")
|
||||
class ButtonScanCard : IntroductionProcess("Button - Scan Card")
|
||||
class CardWasScanned : IntroductionProcess("Card Was Scanned")
|
||||
class ButtonRequestSupport : IntroductionProcess("Button - Request Support")
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ sealed class MainScreen(
|
|||
class ScreenOpened : MainScreen("Screen opened")
|
||||
|
||||
class ButtonScanCard : MainScreen("Button - Scan Card")
|
||||
class CardWasScanned : MainScreen("Card Was Scanned")
|
||||
class ButtonMyWallets : MainScreen("Button - My Wallets")
|
||||
|
||||
class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ sealed class MyWallets(
|
|||
) : AnalyticsEvent("My Wallets", event, params) {
|
||||
|
||||
class MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened")
|
||||
class CardWasScanned : MyWallets(event = "Card Was Scanned")
|
||||
|
||||
sealed class Button {
|
||||
class ScanNewCard : MyWallets(event = "Button - Scan New Card")
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ sealed class Onboarding(
|
|||
|
||||
class ButtonBuyCrypto(currency: AnalyticsParam.CurrencyType) : Topup(
|
||||
event = "Button - Buy Crypto",
|
||||
params = mapOf("Currency" to currency.value),
|
||||
params = mapOf(AnalyticsParam.Currency to currency.value),
|
||||
)
|
||||
|
||||
class ButtonShowWalletAddress : Topup("Button - Show the Wallet Address")
|
||||
|
|
@ -67,8 +67,10 @@ sealed class Onboarding(
|
|||
class SetupFinished : Twins("Twin Setup Finished")
|
||||
}
|
||||
|
||||
class PinScreenOpened : Onboarding("Onboarding", "PIN screen opened")
|
||||
class PinCodeSet : Onboarding("Onboarding", "PIN code set")
|
||||
class ButtonConnect : Onboarding("Onboarding", "Button - Connect")
|
||||
class KYCScreenOpened : Onboarding("Onboarding", "KYC screen opened")
|
||||
class KYCStarted : Onboarding("Onboarding", "KYC started")
|
||||
class KYCInProgress : Onboarding("Onboarding", "KYC in progress")
|
||||
class KYCRejected : Onboarding("Onboarding", "KYC rejected")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ sealed class SignIn(
|
|||
) : 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")
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.paramsInterceptor
|
||||
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BatchIdParamsInterceptor(
|
||||
val batchId: String,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
override fun id(): String = this::class.java.simpleName
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true
|
||||
|
||||
override fun intercept(params: MutableMap<String, String>) {
|
||||
params[AnalyticsParam.BatchId] = batchId
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.tap.common.analytics.paramsInterceptor
|
||||
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.domain.common.ProductType
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CardContextInterceptor(
|
||||
private val scanResponse: ScanResponse?,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
override fun id(): String = CardContextInterceptor.id()
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true
|
||||
|
||||
override fun intercept(params: MutableMap<String, String>) {
|
||||
scanResponse ?: return
|
||||
|
||||
val card = scanResponse.card
|
||||
params[AnalyticsParam.Batch] = card.batchId
|
||||
params[AnalyticsParam.ProductType] = getProductType(scanResponse)
|
||||
params[AnalyticsParam.Firmware] = card.firmwareVersion.stringValue
|
||||
|
||||
ParamCardCurrencyConverter().convert(scanResponse)?.let {
|
||||
params[AnalyticsParam.Currency] = it.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProductType(scanResponse: ScanResponse): String {
|
||||
return when (scanResponse.productType) {
|
||||
ProductType.Note -> "Note"
|
||||
ProductType.Twins -> "Twin"
|
||||
ProductType.Wallet -> "Wallet"
|
||||
ProductType.SaltPay -> if (scanResponse.isSaltPayVisa()) {
|
||||
"Visa"
|
||||
} else {
|
||||
"Visa Backup"
|
||||
}
|
||||
ProductType.Start2Coin -> "Start2Coin"
|
||||
else -> if (DemoHelper.isDemoCard(scanResponse)) {
|
||||
if (DemoHelper.isTestDemoCard(scanResponse)) {
|
||||
"Demo Test"
|
||||
} else {
|
||||
when (scanResponse.card.cardId.substring(0..1)) {
|
||||
"AC" -> "Demo Wallet"
|
||||
"AB" -> "Demo Note"
|
||||
else -> "Demo Other"
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"Other"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun id(): String = CardContextInterceptor::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun Analytics.addCardContext(scanResponse: ScanResponse) {
|
||||
addParamsInterceptor(CardContextInterceptor(scanResponse))
|
||||
}
|
||||
|
||||
fun Analytics.eraseCardContext() {
|
||||
removeParamsInterceptor(CardContextInterceptor.id())
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ 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.paramsInterceptor.BatchIdParamsInterceptor
|
||||
import com.tangem.tap.common.extensions.addCardContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -75,7 +75,7 @@ object ScanCardProcessor {
|
|||
tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse)
|
||||
|
||||
onScanStateChange(false)
|
||||
sendAnalytics(analyticsEvent, scanResponse.card.batchId)
|
||||
sendAnalytics(analyticsEvent, scanResponse)
|
||||
|
||||
checkForUnfinishedBackupForSaltPay(
|
||||
backupService = backupService,
|
||||
|
|
@ -103,9 +103,9 @@ object ScanCardProcessor {
|
|||
|
||||
private fun sendAnalytics(
|
||||
analyticsEvent: AnalyticsEvent?,
|
||||
batchId: String,
|
||||
scanResponse: ScanResponse,
|
||||
) {
|
||||
Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId))
|
||||
Analytics.addCardContext(scanResponse)
|
||||
analyticsEvent?.let { Analytics.send(it) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ object DemoHelper {
|
|||
|
||||
fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId)
|
||||
|
||||
fun isTestDemoCard(scanResponse: ScanResponse): Boolean = config.isTestDemoCardId(scanResponse.card.cardId)
|
||||
|
||||
fun isDemoCardId(cardId: String): Boolean = config.isDemoCardId(cardId)
|
||||
|
||||
fun tryHandle(appState: () -> AppState?, action: Action): Boolean {
|
||||
|
|
@ -108,6 +110,8 @@ class DemoConfig {
|
|||
|
||||
fun isDemoCardId(cardId: String): Boolean = demoCardIds.contains(cardId)
|
||||
|
||||
fun isTestDemoCardId(cardId: String): Boolean = testDemoCardIds.contains(cardId)
|
||||
|
||||
fun getBalance(blockchain: Blockchain): Amount = walletBalances[blockchain]?.copy()
|
||||
?: Amount(BigDecimal.ZERO, blockchain).copy()
|
||||
|
||||
|
|
@ -117,9 +121,6 @@ class DemoConfig {
|
|||
|
||||
private val releaseDemoCardIds = mutableListOf(
|
||||
// === Not from the Google Sheet table ===
|
||||
"FB10000000000196", // Note BTC
|
||||
"FB20000000000186", // Note ETH
|
||||
"FB30000000000176", // Wallet
|
||||
"AC01000000041225",
|
||||
"AC01000000041472",
|
||||
"AB01000000046498",
|
||||
|
|
@ -489,17 +490,19 @@ class DemoConfig {
|
|||
|
||||
class DemoTransactionSender(
|
||||
private val walletManager: WalletManager,
|
||||
private val sender: TransactionSender = walletManager as TransactionSender
|
||||
private val sender: TransactionSender = walletManager as TransactionSender,
|
||||
) : TransactionSender {
|
||||
|
||||
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
return when (walletManager) {
|
||||
is BitcoinWalletManager -> Result.Success(listOf(
|
||||
Amount(0.0001.toBigDecimal(), blockchain),
|
||||
Amount(0.0003.toBigDecimal(), blockchain),
|
||||
Amount(0.00055.toBigDecimal(), blockchain),
|
||||
))
|
||||
is BitcoinWalletManager -> Result.Success(
|
||||
listOf(
|
||||
Amount(0.0001.toBigDecimal(), blockchain),
|
||||
Amount(0.0003.toBigDecimal(), blockchain),
|
||||
Amount(0.00055.toBigDecimal(), blockchain),
|
||||
),
|
||||
)
|
||||
else -> sender.getFee(amount, destination)
|
||||
}
|
||||
}
|
||||
|
|
@ -508,7 +511,7 @@ class DemoTransactionSender(
|
|||
val dataToSign = randomString(32).toByteArray()
|
||||
val signerResponse = signer.sign(
|
||||
hash = dataToSign,
|
||||
publicKey = walletManager.wallet.publicKey
|
||||
publicKey = walletManager.wallet.publicKey,
|
||||
)
|
||||
return when (signerResponse) {
|
||||
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainSdkError())
|
||||
|
|
@ -529,5 +532,4 @@ class DemoTransactionSender(
|
|||
companion object {
|
||||
val ID = DemoTransactionSender::class.java.simpleName
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Analytics.send(IntroductionProcess.ScreenOpened())
|
||||
store.dispatch(HomeAction.OnCreate)
|
||||
store.dispatch(HomeAction.Init)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +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.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class HomeAction : Action {
|
||||
|
||||
object OnCreate : HomeAction()
|
||||
object Init : HomeAction()
|
||||
|
||||
data class ReadCard(
|
||||
val analyticsEvent: AnalyticsEvent? = IntroductionProcess.CardWasScanned(),
|
||||
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
|
||||
) : HomeAction()
|
||||
|
||||
data class ScanInProgress(val scanInProgress: Boolean) : HomeAction()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ 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.IntroductionProcess
|
||||
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.eraseCardContext
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -51,6 +53,10 @@ private val homeMiddleware: Middleware<AppState> = { _, _ ->
|
|||
|
||||
private fun handleHomeAction(action: Action) {
|
||||
when (action) {
|
||||
is HomeAction.OnCreate -> {
|
||||
Analytics.eraseCardContext()
|
||||
Analytics.send(IntroductionProcess.ScreenOpened())
|
||||
}
|
||||
is HomeAction.Init -> {
|
||||
store.dispatch(GlobalAction.RestoreAppCurrency)
|
||||
store.dispatch(GlobalAction.ExchangeManager.Init)
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
|
|||
}
|
||||
is OnboardingSaltPayAction.SetStep -> {
|
||||
when (action.newStep) {
|
||||
SaltPayActivationStep.NeedPin -> Analytics.send(Onboarding.PinScreenOpened())
|
||||
SaltPayActivationStep.KycIntro -> Analytics.send(Onboarding.KYCScreenOpened())
|
||||
SaltPayActivationStep.KycStart -> Analytics.send(Onboarding.KYCStarted())
|
||||
SaltPayActivationStep.KycWaiting -> Analytics.send(Onboarding.KYCInProgress())
|
||||
SaltPayActivationStep.KycReject -> Analytics.send(Onboarding.KYCRejected())
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ 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.Basic
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
|
|
@ -378,7 +379,7 @@ class WalletMiddleware {
|
|||
}
|
||||
else -> {
|
||||
Analytics.send(MainScreen.ButtonScanCard())
|
||||
store.dispatch(WalletAction.Scan(MainScreen.CardWasScanned()))
|
||||
store.dispatch(WalletAction.Scan(Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet.saltPay
|
||||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class HistoryTransactionData(
|
||||
val transactionData: TransactionData,
|
||||
private val walletAddress: String,
|
||||
) {
|
||||
fun isInProgress(): Boolean = transactionData.status == TransactionStatus.Unconfirmed
|
||||
|
||||
fun getTransactionType(): PendingTransactionType = when {
|
||||
transactionData.sourceAddress.lowercase() == walletAddress.lowercase() -> PendingTransactionType.Outgoing
|
||||
transactionData.destinationAddress.lowercase() == walletAddress.lowercase() -> PendingTransactionType.Incoming
|
||||
else -> PendingTransactionType.Unknown
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet.saltPay.rv
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class ShimmerRecyclerAdapter(
|
||||
private val viewHolderViewFactory: (ViewGroup) -> ViewGroup,
|
||||
) : ListAdapter<ShimmerData, ShimmerVH>(DiffUtilCallback) {
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return if (currentList.isEmpty()) 0 else currentList[position].hashCode().toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShimmerVH {
|
||||
return ShimmerVH(viewHolderViewFactory.invoke(parent))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ShimmerVH, position: Int) {}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<ShimmerData>() {
|
||||
override fun areContentsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem
|
||||
override fun areItemsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
class ShimmerVH(viewGroup: ViewGroup) : RecyclerView.ViewHolder(viewGroup)
|
||||
|
||||
data class ShimmerData(private val any: String = "")
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet.saltPay.rv
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.setDrawable
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.ui.wallet.saltPay.HistoryTransactionData
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ItemSaltPayTxHistoryBinding
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class TransactionHistoryAdapter : ListAdapter<HistoryTransactionData, TransactionItemVH>(DiffUtilCallback) {
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return if (currentList.isEmpty()) 0
|
||||
else currentList[position].transactionData.hash.hashCode().toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionItemVH {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val layout = ItemSaltPayTxHistoryBinding.inflate(inflater, parent, false)
|
||||
return TransactionItemVH(layout)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TransactionItemVH, position: Int) {
|
||||
holder.bind(currentList[position])
|
||||
}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<HistoryTransactionData>() {
|
||||
override fun areContentsTheSame(
|
||||
oldItem: HistoryTransactionData,
|
||||
newItem: HistoryTransactionData,
|
||||
) = oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: HistoryTransactionData,
|
||||
newItem: HistoryTransactionData,
|
||||
) = oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
internal class TransactionItemVH(
|
||||
private val binding: ItemSaltPayTxHistoryBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(data: HistoryTransactionData) = with(binding) {
|
||||
setupImage(data)
|
||||
setupTitle(data)
|
||||
setupSubtitle(data)
|
||||
setupBalance(data)
|
||||
}
|
||||
|
||||
private fun setupImage(data: HistoryTransactionData) = with(binding) {
|
||||
val drawable = when (data.getTransactionType()) {
|
||||
PendingTransactionType.Incoming -> R.drawable.ic_tx_incoming
|
||||
PendingTransactionType.Outgoing -> R.drawable.ic_tx_outgoing
|
||||
PendingTransactionType.Unknown -> null
|
||||
}
|
||||
drawable?.let { imvTx.setDrawable(it) }
|
||||
|
||||
if (data.isInProgress()) {
|
||||
imvTx.setColorFilter(imvTx.getColor(R.color.icon_attention), PorterDuff.Mode.MULTIPLY)
|
||||
} else {
|
||||
imvTx.clearColorFilter()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTitle(data: HistoryTransactionData) = with(binding) {
|
||||
val hash = data.transactionData.hash ?: return@with
|
||||
|
||||
val formattedHash = "${hash.substring(0..5)}...${hash.substring(hash.length - 4)}"
|
||||
tvTxHash.text = formattedHash
|
||||
}
|
||||
|
||||
private fun setupSubtitle(data: HistoryTransactionData) = with(binding) {
|
||||
val inProgress = data.isInProgress()
|
||||
tvTxStatus.show(inProgress)
|
||||
tvTxTime.show(!inProgress)
|
||||
|
||||
val time = data.transactionData.date?.timeInMillis.toString() ?: "none"
|
||||
tvTxTime.text = time
|
||||
}
|
||||
|
||||
private fun setupBalance(data: HistoryTransactionData) = with(binding) {
|
||||
val sign = when (data.getTransactionType()) {
|
||||
PendingTransactionType.Incoming -> "+"
|
||||
PendingTransactionType.Outgoing -> "-"
|
||||
PendingTransactionType.Unknown -> ""
|
||||
}
|
||||
val amount = data.transactionData.amount
|
||||
tvTxAmountSign.text = sign
|
||||
tvTxAmountValue.text = amount.value?.toFormattedCurrencyString(8, amount.currencySymbol)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,10 @@ import com.tangem.common.map
|
|||
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.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.MyWallets
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
|
||||
import com.tangem.tap.common.extensions.addCardContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -136,7 +138,7 @@ internal class WalletSelectorMiddleware {
|
|||
)
|
||||
|
||||
ScanCardProcessor.scan(
|
||||
analyticsEvent = MyWallets.CardWasScanned(),
|
||||
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets),
|
||||
onWalletNotCreated = {
|
||||
// No need to rollback policy, continue with the policy set before the card scan
|
||||
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
|
||||
|
|
@ -197,8 +199,7 @@ internal class WalletSelectorMiddleware {
|
|||
.doOnSuccess {
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
val batchId = selectedUserWallet.scanResponse.card.batchId
|
||||
Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId))
|
||||
Analytics.addCardContext(selectedUserWallet.scanResponse)
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.core.TangemError
|
|||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface WelcomeAction : Action {
|
||||
object OnCreate: WelcomeAction
|
||||
object ProceedWithBiometrics : WelcomeAction {
|
||||
object Success : WelcomeAction
|
||||
data class Error(val error: TangemError) : WelcomeAction
|
||||
|
|
|
|||
|
|
@ -4,9 +4,14 @@ import android.content.Intent
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
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.events.SignIn
|
||||
import com.tangem.tap.common.extensions.addCardContext
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.eraseCardContext
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -38,6 +43,10 @@ internal class WelcomeMiddleware {
|
|||
|
||||
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
|
||||
when (action) {
|
||||
is WelcomeAction.OnCreate -> {
|
||||
Analytics.eraseCardContext()
|
||||
Analytics.send(SignIn.ScreenOpened())
|
||||
}
|
||||
is WelcomeAction.ProceedWithBiometrics -> {
|
||||
proceedWithBiometry(state)
|
||||
}
|
||||
|
|
@ -62,6 +71,7 @@ internal class WelcomeMiddleware {
|
|||
}
|
||||
.doOnSuccess { selectedUserWallet ->
|
||||
if (selectedUserWallet != null) {
|
||||
Analytics.addCardContext(selectedUserWallet.scanResponse)
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success)
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
|
|
@ -101,7 +111,7 @@ internal class WelcomeMiddleware {
|
|||
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
|
||||
)
|
||||
ScanCardProcessor.scan(
|
||||
analyticsEvent = SignIn.CardWasScanned(),
|
||||
analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn),
|
||||
onSuccess = { scanResponse ->
|
||||
scope.launch { onCardScanned(scanResponse) }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ internal object WelcomeReducer {
|
|||
is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false)
|
||||
is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false)
|
||||
is WelcomeAction.CloseError -> state.copy(error = null)
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,13 +19,13 @@ 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.redux.WelcomeAction
|
||||
import com.tangem.tap.features.welcome.ui.components.WelcomeScreenContent
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
|
||||
|
|
@ -33,7 +33,7 @@ internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Analytics.send(SignIn.ScreenOpened())
|
||||
store.dispatch(WelcomeAction.OnCreate)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ object Analytics : GlobalAnalyticsEventHandler {
|
|||
paramsInterceptors[interceptor.id()] = interceptor
|
||||
}
|
||||
|
||||
override fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor? {
|
||||
return paramsInterceptors.remove(interceptor.id())
|
||||
override fun removeParamsInterceptor(interceptorId: String): ParamsInterceptor? {
|
||||
return paramsInterceptors.remove(interceptorId)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,5 @@ interface ParamsInterceptor {
|
|||
|
||||
interface ParamsInterceptorHolder {
|
||||
fun addParamsInterceptor(interceptor: ParamsInterceptor)
|
||||
fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor?
|
||||
fun removeParamsInterceptor(interceptorId: String): ParamsInterceptor?
|
||||
}
|
||||
|
|
@ -22,5 +22,5 @@ object NetworkLogConfig {
|
|||
object AnalyticsHandlersLogConfig {
|
||||
val firebase: Boolean = false
|
||||
val appsFlyer: Boolean = false
|
||||
val amplitude: Boolean = false
|
||||
val amplitude: Boolean = true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue