Updated on 2026-08-14
This commit is contained in:
parent
bf1989f43f
commit
8a90697079
15 changed files with 116 additions and 19 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit a5f541fa4380cf430144fab04e42b32b033ff548
|
||||
Subproject commit c714678d9c46dee1b15ef9e8d27aa23afa1dfd8d
|
||||
|
|
@ -24,6 +24,8 @@ sealed class AnalyticsParam {
|
|||
sealed class CardBalanceState(val value: String) {
|
||||
object Empty : CardBalanceState("Empty")
|
||||
object Full : CardBalanceState("Full")
|
||||
object CustomToken : CardBalanceState("Custom token")
|
||||
object BlockchainError : CardBalanceState("Blockchain error")
|
||||
companion object
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +81,7 @@ sealed class AnalyticsParam {
|
|||
|
||||
companion object Key {
|
||||
const val Source = "Source"
|
||||
const val Balance = "Balance"
|
||||
const val Batch = "Batch"
|
||||
const val ProductType = "Product Type"
|
||||
const val Firmware = "Firmware"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ sealed class Basic(
|
|||
error: Throwable? = null,
|
||||
) : AnalyticsEvent("Basic", event, params, error) {
|
||||
|
||||
class BalanceLoaded(balance: AnalyticsParam.CardBalanceState) : Basic(
|
||||
event = "Balance Loaded",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Balance to balance.value,
|
||||
),
|
||||
)
|
||||
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScannedFrom,
|
||||
) : Basic(
|
||||
|
|
@ -47,4 +54,6 @@ sealed class Basic(
|
|||
event = "Scan",
|
||||
error = error,
|
||||
)
|
||||
|
||||
class WalletOpened : Basic(event = "Wallet Opened")
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ sealed class MainScreen(
|
|||
) : AnalyticsEvent("Main Screen", event, params) {
|
||||
|
||||
class ScreenOpened : MainScreen("Screen opened")
|
||||
|
||||
class ButtonScanCard : MainScreen("Button - Scan Card")
|
||||
class ButtonMyWallets : MainScreen("Button - My Wallets")
|
||||
class ButtonBuy : MainScreen("Button - Buy")
|
||||
|
|
|
|||
|
|
@ -71,16 +71,16 @@ sealed class Onboarding(
|
|||
class ButtonSetPinCode : Onboarding("Onboarding", "Button - Set PIN Code")
|
||||
class CardConnectionScreenOpened : Onboarding("Onboarding", "Card Connection Screen Opened")
|
||||
class ButtonConnect : Onboarding("Onboarding", "Button - Connect")
|
||||
class PinCodeSet : Onboarding("Onboarding", "PIN code set")
|
||||
class PinCodeSet : Onboarding("Onboarding", "PIN Code Set")
|
||||
|
||||
class KYCScreenOpened : Onboarding("Onboarding", "KYC screen opened")
|
||||
class KYCStarted : Onboarding("Onboarding", "KYC started")
|
||||
class KYCInProgress : Onboarding("Onboarding", "KYC in progress")
|
||||
class KYCRejected : Onboarding("Onboarding", "KYC rejected")
|
||||
class KYCStarted : Onboarding("Onboarding", "KYC Started")
|
||||
class KYCInProgress : Onboarding("Onboarding", "KYC In Progress")
|
||||
class KYCRejected : Onboarding("Onboarding", "KYC Rejected")
|
||||
|
||||
class ClaimScreenOpened : Onboarding("Onboarding", "Claim screen opened")
|
||||
class ClaimScreenOpened : Onboarding("Onboarding", "Claim Screen Opened")
|
||||
class ButtonClaim : Onboarding("Onboarding", "Button - Claim")
|
||||
class ClaimWasSuccessfully : Onboarding("Onboarding", "Claim was successfully")
|
||||
class ClaimWasSuccessfully : Onboarding("Onboarding", "Claim Was Successfully")
|
||||
|
||||
class ButtonChat : Onboarding("Onboarding", "Button - Chat")
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ sealed class Token(
|
|||
|
||||
class Bought(type: CurrencyType) : Token(
|
||||
category = "Token",
|
||||
event = "Token bought",
|
||||
event = "Token Bought",
|
||||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,12 @@ fun Store<*>.dispatchNotification(resId: Int) {
|
|||
dispatchOnMain(GlobalAction.ShowNotification(resId))
|
||||
}
|
||||
|
||||
suspend fun Store<AppState>.onUserWalletSelected(userWallet: UserWallet, refresh: Boolean = false) {
|
||||
state.globalState.tapWalletManager.onWalletSelected(userWallet, refresh)
|
||||
suspend fun Store<AppState>.onUserWalletSelected(
|
||||
userWallet: UserWallet,
|
||||
refresh: Boolean = false,
|
||||
sendAnalyticsEvent: Boolean = false,
|
||||
) {
|
||||
state.globalState.tapWalletManager.onWalletSelected(userWallet, refresh, sendAnalyticsEvent)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchToastNotification(resId: Int) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.datasource.config.ConfigManager
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.setContext
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -37,8 +38,11 @@ class TapWalletManager {
|
|||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean) {
|
||||
suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) {
|
||||
Analytics.setContext(userWallet.scanResponse)
|
||||
if (sendAnalyticsEvent) {
|
||||
Analytics.send(Basic.WalletOpened())
|
||||
}
|
||||
val scanResponse = userWallet.scanResponse
|
||||
val card = scanResponse.card
|
||||
val attestationFailed = card.attestation.status == Attestation.Status.Failed
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.transition.TransitionInflater
|
|||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import coil.load
|
||||
import coil.size.Scale
|
||||
import com.badoo.mvicore.modelWatcher
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.ui.fragments.setStatusBarColor
|
||||
import com.tangem.core.ui.utils.OneTouchClickListener
|
||||
|
|
@ -68,9 +69,17 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
|
||||
private val viewModel by viewModels<WalletViewModel>()
|
||||
|
||||
private val totalBalanceWatcher = modelWatcher {
|
||||
(WalletState::totalBalance) { totalBalance ->
|
||||
totalBalance?.state?.let {
|
||||
viewModel.onBalanceLoaded(totalBalance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
activity?.lifecycleScope?.launchWhenCreated {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
viewModel.launch()
|
||||
}
|
||||
}
|
||||
|
|
@ -172,6 +181,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
}
|
||||
else -> {} // we keep the same view unless we scan a card that requires a different view
|
||||
}
|
||||
totalBalanceWatcher.invoke(state)
|
||||
|
||||
walletView.swapInteractor = swapInteractor
|
||||
walletView.swapFeatureToggleManager = swapFeatureToggleManager
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ package com.tangem.tap.features.wallet.ui
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -16,16 +21,22 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.StoreSubscriber
|
||||
import javax.inject.Inject
|
||||
|
||||
// TODO: Kill me, please
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class WalletViewModel : ViewModel(), StoreSubscriber<UserWalletsListManager?> {
|
||||
@HiltViewModel
|
||||
internal class WalletViewModel @Inject constructor(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : ViewModel(), StoreSubscriber<UserWalletsListManager?> {
|
||||
private var observeWalletStoresUpdatesJob: Job? = null
|
||||
set(value) {
|
||||
field?.cancel()
|
||||
field = value
|
||||
}
|
||||
|
||||
private val walletAnalyticsEventsMapper = WalletAnalyticsEventsMapper()
|
||||
|
||||
init {
|
||||
subscribeToUserWalletsListManagerUpdates()
|
||||
}
|
||||
|
|
@ -49,6 +60,18 @@ internal class WalletViewModel : ViewModel(), StoreSubscriber<UserWalletsListMan
|
|||
bootstrapShowSaveWalletIfNeeded()
|
||||
}
|
||||
|
||||
fun onBalanceLoaded(totalBalance: TotalBalance?) {
|
||||
if (totalBalance != null) {
|
||||
walletAnalyticsEventsMapper.convert(totalBalance)?.let { balanceParam ->
|
||||
analyticsEventHandler.send(
|
||||
Basic.BalanceLoaded(
|
||||
balance = balanceParam,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bootstrapSelectedWalletStoresChanges(manager: UserWalletsListManager) {
|
||||
observeWalletStoresUpdatesJob = manager.selectedUserWallet
|
||||
.map { it.walletId }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tap.features.wallet.ui.analytics
|
||||
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.isGreaterThan
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
class WalletAnalyticsEventsMapper : Converter<TotalBalance, AnalyticsParam.CardBalanceState?> {
|
||||
|
||||
override fun convert(value: TotalBalance): AnalyticsParam.CardBalanceState? {
|
||||
return when (value.state) {
|
||||
ProgressState.Done -> if (value.fiatAmount?.isGreaterThan(BigDecimal.ZERO) == true) {
|
||||
AnalyticsParam.CardBalanceState.Full
|
||||
} else {
|
||||
AnalyticsParam.CardBalanceState.Empty
|
||||
}
|
||||
ProgressState.Error -> {
|
||||
if (value.fiatAmount == null) {
|
||||
// if fiatAmount is null while ProgressState.Error it means error occurs when loading blockchain
|
||||
AnalyticsParam.CardBalanceState.BlockchainError
|
||||
} else {
|
||||
// if fiatAmount is not null while ProgressState.Error it means couldn't load custom token amount
|
||||
AnalyticsParam.CardBalanceState.CustomToken
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ internal sealed interface WalletSelectorAction : Action {
|
|||
|
||||
data class SelectWallet(
|
||||
val userWalletId: UserWalletId,
|
||||
val sendAnalyticsEvent: Boolean = false,
|
||||
) : WalletSelectorAction
|
||||
|
||||
data class RenameWallet(
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ internal class WalletSelectorMiddleware {
|
|||
addWallet()
|
||||
}
|
||||
is WalletSelectorAction.SelectWallet -> {
|
||||
selectWallet(action.userWalletId)
|
||||
selectWallet(action.userWalletId, action.sendAnalyticsEvent)
|
||||
}
|
||||
is WalletSelectorAction.RemoveWallets -> {
|
||||
deleteWallets(action.userWalletsIds, state)
|
||||
|
|
@ -189,7 +189,7 @@ internal class WalletSelectorMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectWallet(userWalletId: UserWalletId) {
|
||||
private fun selectWallet(userWalletId: UserWalletId, sendAnalyticsEvent: Boolean) {
|
||||
scope.launch {
|
||||
userWalletsListManager.get(userWalletId)
|
||||
.flatMap { userWallet ->
|
||||
|
|
@ -212,7 +212,10 @@ internal class WalletSelectorMiddleware {
|
|||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
|
||||
if (selectedUserWallet != null) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
store.onUserWalletSelected(
|
||||
userWallet = selectedUserWallet,
|
||||
sendAnalyticsEvent = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -336,7 +339,10 @@ internal class WalletSelectorMiddleware {
|
|||
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
|
||||
}
|
||||
currentSelectedWalletId != selectedUserWallet.walletId -> {
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
store.onUserWalletSelected(
|
||||
userWallet = selectedUserWallet,
|
||||
sendAnalyticsEvent = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ import com.tangem.tap.features.walletSelector.ui.components.RenameWalletDialogCo
|
|||
import com.tangem.tap.features.walletSelector.ui.components.WalletSelectorScreenContent
|
||||
import com.tangem.tap.features.walletSelector.ui.model.DialogModel
|
||||
import com.tangem.tap.features.walletSelector.ui.model.WarningModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment<WalletSelectorScreenState>() {
|
||||
private val viewModel by viewModels<WalletSelectorViewModel>()
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,12 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber<WalletSele
|
|||
cancelWalletEditing(userWalletId)
|
||||
}
|
||||
selectedUserWalletId != userWalletId -> {
|
||||
store.dispatch(WalletSelectorAction.SelectWallet(userWalletId))
|
||||
store.dispatch(
|
||||
WalletSelectorAction.SelectWallet(
|
||||
userWalletId = userWalletId,
|
||||
sendAnalyticsEvent = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue