Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-20 11:45:42 +03:00
parent 7b534690ff
commit 864d524841
10 changed files with 301 additions and 14 deletions

View file

@ -1,10 +1,10 @@
package com.tangem.feature.wallet.presentation.organizetokens
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.*
import arrow.core.getOrElse
import com.tangem.common.Provider
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
@ -13,6 +13,7 @@ import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase
import com.tangem.domain.tokens.ToggleTokenListSortingUseCase
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.organizetokens.analytics.PortfolioOrganizeTokensAnalyticsEvent
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
import com.tangem.feature.wallet.presentation.organizetokens.utils.CryptoCurrenciesIdsResolver
@ -35,9 +36,10 @@ internal class OrganizeTokensViewModel @Inject constructor(
private val toggleTokenListSortingUseCase: ToggleTokenListSortingUseCase,
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val analyticsEventsHandler: AnalyticsEventHandler,
private val dispatchers: CoroutineDispatcherProvider,
savedStateHandle: SavedStateHandle,
) : ViewModel(), OrganizeTokensIntents {
) : ViewModel(), DefaultLifecycleObserver, OrganizeTokensIntents {
lateinit var router: InnerWalletRouter
@ -68,11 +70,17 @@ internal class OrganizeTokensViewModel @Inject constructor(
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
override fun onCreate(owner: LifecycleOwner) {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ScreenOpened)
}
override fun onBackClick() {
router.popBackStack()
}
override fun onSortClick() {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
viewModelScope.launch(dispatchers.default) {
val list = tokenList ?: return@launch
@ -87,6 +95,8 @@ internal class OrganizeTokensViewModel @Inject constructor(
}
override fun onGroupClick() {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
viewModelScope.launch(dispatchers.default) {
val list = tokenList ?: return@launch
@ -107,11 +117,19 @@ internal class OrganizeTokensViewModel @Inject constructor(
val listState = uiState.value.itemsState
val resolver = CryptoCurrenciesIdsResolver()
val isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork
val isSortedByBalance = uiState.value.header.isSortedByBalance
sendAnalyticsEvent(
isGroupedByNetwork = isGroupedByNetwork,
isSortedByBalance = isSortedByBalance,
)
val result = applyTokenListSortingUseCase(
userWalletId = userWalletId,
sortedTokensIds = resolver.resolve(listState, tokenList),
isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork,
isSortedByBalance = uiState.value.header.isSortedByBalance,
isGroupedByNetwork = isGroupedByNetwork,
isSortedByBalance = isSortedByBalance,
)
result.fold(
@ -127,6 +145,8 @@ internal class OrganizeTokensViewModel @Inject constructor(
}
override fun onCancelClick() {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Cancel)
router.popBackStack()
}
@ -166,4 +186,21 @@ internal class OrganizeTokensViewModel @Inject constructor(
initialValue = AppCurrency.Default,
)
}
private fun sendAnalyticsEvent(isGroupedByNetwork: Boolean, isSortedByBalance: Boolean) {
analyticsEventsHandler.send(
PortfolioOrganizeTokensAnalyticsEvent.Apply(
grouping = if (isGroupedByNetwork) {
AnalyticsParam.OnOffState.On
} else {
AnalyticsParam.OnOffState.Off
},
organizeSortType = if (isSortedByBalance) {
AnalyticsParam.OrganizeSortType.ByBalance
} else {
AnalyticsParam.OrganizeSortType.Manually
},
),
)
}
}

View file

@ -0,0 +1,29 @@
package com.tangem.feature.wallet.presentation.organizetokens.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
sealed class PortfolioOrganizeTokensAnalyticsEvent(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Portfolio / Organize Tokens", event, params) {
object ScreenOpened : PortfolioOrganizeTokensAnalyticsEvent("Organize Tokens Screen Opened")
object ByBalance : PortfolioOrganizeTokensAnalyticsEvent("Button - By Balance")
object Group : PortfolioOrganizeTokensAnalyticsEvent("Button - Group")
class Apply(
grouping: AnalyticsParam.OnOffState,
organizeSortType: AnalyticsParam.OrganizeSortType,
) : PortfolioOrganizeTokensAnalyticsEvent(
"Button - Apply",
params = mapOf(
"Group" to grouping.value,
"Sort" to organizeSortType.value,
),
)
object Cancel : PortfolioOrganizeTokensAnalyticsEvent("Button - Cancel")
}

View file

@ -0,0 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
sealed class PortfolioEvent(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Portfolio", event, params) {
object Refreshed : PortfolioEvent("Refreshed")
object ButtonManageTokens : PortfolioEvent("Button - Manage Tokens")
object TokenTapped : PortfolioEvent("Token is Tapped")
object OrganizeTokens : PortfolioEvent("Button - Organize Tokens")
}

View file

@ -0,0 +1,28 @@
package com.tangem.feature.wallet.presentation.wallet.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
sealed class WalletScreenAnalyticsEvent(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Main Screen", event, params) {
object ScreenOpened : WalletScreenAnalyticsEvent("Screen opened")
object WalletSwipe : WalletScreenAnalyticsEvent("Wallet Swipe")
class EnableBiometrics(state: AnalyticsParam.OnOffState) : WalletScreenAnalyticsEvent(
event = "Enable Biometric",
params = mapOf("State" to state.value),
)
// TODO [REDACTED_JIRA]
class NoticeRateAppButton(result: AnalyticsParam.RateApp) : WalletScreenAnalyticsEvent(
event = "Notice - Rate The App Button Tapped",
params = mapOf("Result" to result.value),
)
object NoticeBackupYourWalletTapped : WalletScreenAnalyticsEvent("Notice - Backup Your Wallet Tapped")
object NoticeScanYourCardTapped : WalletScreenAnalyticsEvent("Notice - Scan Your Card Tapped")
object NoticeWalletLocked : WalletScreenAnalyticsEvent("Notice - Wallet Locked")
}

View file

@ -6,6 +6,7 @@ import arrow.core.getOrElse
import com.tangem.common.Provider
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.navigation.AppScreen
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
@ -37,6 +38,8 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.state.*
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
@ -93,6 +96,7 @@ internal class WalletViewModel @Inject constructor(
private val walletManagersFacade: WalletManagersFacade,
private val reduxStateHolder: ReduxStateHolder,
private val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventsHandler: AnalyticsEventHandler,
// endregion Parameters
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
@ -141,6 +145,8 @@ internal class WalletViewModel @Inject constructor(
)
override fun onCreate(owner: LifecycleOwner) {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.ScreenOpened)
viewModelScope.launch(dispatchers.main) {
delay(timeMillis = 1_800)
@ -231,6 +237,8 @@ internal class WalletViewModel @Inject constructor(
}
override fun onScanCardClick() {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.NoticeScanYourCardTapped)
viewModelScope.launch(dispatchers.io) {
scanCardProcessor.scan()
.doOnSuccess {
@ -330,7 +338,10 @@ internal class WalletViewModel @Inject constructor(
override fun onDetailsClick() = router.openDetailsScreen()
override fun onBackupCardClick() = router.openOnboardingScreen()
override fun onBackupCardClick() {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.NoticeBackupYourWalletTapped)
router.openOnboardingScreen()
}
override fun onCriticalWarningAlreadySignedHashesClick() {
uiState = stateFactory.getStateWithOpenWalletBottomSheet(
@ -363,6 +374,8 @@ internal class WalletViewModel @Inject constructor(
}
override fun onWalletChange(index: Int) {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.WalletSwipe)
val state = uiState as? WalletState.ContentState ?: return
if (state.walletsListConfig.selectedWalletIndex == index) return
@ -417,8 +430,14 @@ internal class WalletViewModel @Inject constructor(
?: return
when (uiState) {
is WalletMultiCurrencyState.Content -> refreshMultiCurrencyContent(selectedWalletIndex)
is WalletSingleCurrencyState.Content -> refreshSingleCurrencyContent(selectedWalletIndex)
is WalletMultiCurrencyState.Content -> {
analyticsEventsHandler.send(PortfolioEvent.Refreshed)
refreshMultiCurrencyContent(selectedWalletIndex)
}
is WalletSingleCurrencyState.Content -> {
analyticsEventsHandler.send(PortfolioEvent.Refreshed)
refreshSingleCurrencyContent(selectedWalletIndex)
}
is WalletState.Initial,
is WalletMultiCurrencyState.Locked,
is WalletSingleCurrencyState.Locked,
@ -427,6 +446,8 @@ internal class WalletViewModel @Inject constructor(
}
override fun onOrganizeTokensClick() {
analyticsEventsHandler.send(PortfolioEvent.OrganizeTokens)
val state = requireNotNull(uiState as? WalletState.ContentState)
val index = state.walletsListConfig.selectedWalletIndex
val walletId = state.walletsListConfig.wallets[index].id
@ -533,6 +554,7 @@ internal class WalletViewModel @Inject constructor(
}
override fun onManageTokensClick() {
analyticsEventsHandler.send(PortfolioEvent.ButtonManageTokens)
reduxStateHolder.dispatch(action = TokensAction.SetArgs.ManageAccess)
router.openManageTokensScreen()
}
@ -567,6 +589,8 @@ internal class WalletViewModel @Inject constructor(
}
override fun onUnlockWalletClick() {
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.NoticeWalletLocked)
viewModelScope.launch(dispatchers.io) {
unlockWalletsUseCase()
}
@ -586,6 +610,7 @@ internal class WalletViewModel @Inject constructor(
}
override fun onTokenItemClick(currency: CryptoCurrency) {
analyticsEventsHandler.send(PortfolioEvent.TokenTapped)
router.openTokenDetails(currency = currency)
}