Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-03 20:37:40 +02:00
commit da717845f0
538 changed files with 7350 additions and 5597 deletions

View file

@ -0,0 +1,66 @@
package com.tangem.feature.wallet
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.stack.animation.fade
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.arkivanov.decompose.value.Value
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent
import com.tangem.feature.wallet.child.wallet.WalletComponent
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.features.wallet.WalletEntryComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultWalletEntryComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: Unit,
walletComponentFactory: WalletComponent.Factory,
) : WalletEntryComponent, AppComponentContext by appComponentContext {
private val navigation = StackNavigation<WalletRoute>()
private val stack: Value<ChildStack<WalletRoute, ComposableContentComponent>> = childStack(
source = navigation,
serializer = WalletRoute.serializer(),
initialConfiguration = WalletRoute.Wallet,
childFactory = { route, context ->
when (route) {
WalletRoute.Wallet -> walletComponentFactory.create(
appComponentContext = childByContext(context),
navigate = { navigation.pushNew(it) },
)
is WalletRoute.OrganizeTokens -> OrganizeTokensComponent(
appComponentContext = childByContext(context),
params = OrganizeTokensComponent.Params(route.userWalletId),
onBack = { navigation.pop() },
)
}
},
)
@Composable
override fun Content(modifier: Modifier) {
Children(
stack = stack,
animation = stackAnimation(fade()),
) {
it.instance.Content(modifier)
}
}
@AssistedFactory
interface Factory : WalletEntryComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultWalletEntryComponent
}
}

View file

@ -0,0 +1,39 @@
package com.tangem.feature.wallet.child.organizetokens
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModel
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
import kotlinx.coroutines.launch
internal class OrganizeTokensComponent(
appComponentContext: AppComponentContext,
params: Params,
onBack: () -> Unit,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val model: OrganizeTokensModel = getOrCreateModel(params)
init {
componentScope.launch {
model.onBack.collect { onBack() }
}
}
data class Params(val userWalletId: UserWalletId)
@Composable
override fun Content(modifier: Modifier) {
val uiState by model.uiState.collectAsStateWithLifecycle()
OrganizeTokensScreen(
state = uiState,
)
}
}

View file

@ -1,9 +1,12 @@
package com.tangem.feature.wallet.presentation.organizetokens
package com.tangem.feature.wallet.child.organizetokens.model
import androidx.lifecycle.*
import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
@ -13,24 +16,27 @@ import com.tangem.domain.tokens.GetTokenListUseCase
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.child.organizetokens.OrganizeTokensComponent
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensIntents
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder
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
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.disableSortingByBalance
import com.tangem.feature.wallet.presentation.organizetokens.utils.dnd.DragAndDropAdapter
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.router.WalletRoute
import com.tangem.utils.Provider
import dagger.hilt.android.lifecycle.HiltViewModel
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@HiltViewModel
internal class OrganizeTokensViewModel @Inject constructor(
@Stable
@ModelScoped
internal class OrganizeTokensModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val getTokenListUseCase: GetTokenListUseCase,
private val toggleTokenListGroupingUseCase: ToggleTokenListGroupingUseCase,
private val toggleTokenListSortingUseCase: ToggleTokenListSortingUseCase,
@ -38,10 +44,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val analyticsEventsHandler: AnalyticsEventHandler,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, OrganizeTokensIntents {
lateinit var router: InnerWalletRouter
) : Model(), OrganizeTokensIntents {
private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow()
@ -57,33 +60,30 @@ internal class OrganizeTokensViewModel @Inject constructor(
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
)
private val userWalletId: UserWalletId by lazy {
val userWalletIdValue: String = checkNotNull(savedStateHandle[WalletRoute.userWalletIdKey])
UserWalletId(userWalletIdValue)
}
private val userWalletId = paramsContainer.require<OrganizeTokensComponent.Params>().userWalletId
private var cachedTokenList: TokenList? = null
val uiState: StateFlow<OrganizeTokensState> = stateHolder.stateFlow
override fun onCreate(owner: LifecycleOwner) {
val onBack = MutableSharedFlow<Unit>()
init {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ScreenOpened)
getBalanceHidingSettingsUseCase()
.flowWithLifecycle(owner.lifecycle)
.onEach {
isBalanceHidden = it.isBalanceHidden
stateHolder.updateHiddenState(isBalanceHidden)
}
.launchIn(viewModelScope)
.launchIn(modelScope)
bootstrapTokenList()
bootstrapDragAndDropUpdates()
}
override fun onBackClick() {
router.popBackStack()
modelScope.launch { onBack.emit(Unit) }
}
override fun onSortClick() {
@ -92,7 +92,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance)
viewModelScope.launch {
modelScope.launch {
toggleTokenListSortingUseCase(list).fold(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
@ -108,7 +108,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group)
viewModelScope.launch {
modelScope.launch {
toggleTokenListGroupingUseCase(list).fold(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
@ -120,7 +120,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
}
override fun onApplyClick() {
viewModelScope.launch {
modelScope.launch {
stateHolder.updateStateToDisplayProgress()
val listState = uiState.value.itemsState
@ -144,7 +144,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
result.fold(
ifLeft = stateHolder::updateStateWithError,
ifRight = {
router.popBackStack()
modelScope.launch { onBack.emit(Unit) }
stateHolder.updateStateToHideProgress()
},
)
@ -154,11 +154,11 @@ internal class OrganizeTokensViewModel @Inject constructor(
override fun onCancelClick() {
analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Cancel)
router.popBackStack()
modelScope.launch { onBack.emit(Unit) }
}
private fun bootstrapTokenList() {
viewModelScope.launch {
modelScope.launch {
val tokenList = getTokenList() ?: return@launch
stateHolder.updateStateWithTokenList(tokenList)
@ -192,7 +192,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
stateHolder.updateStateWithManualSorting(updatedListState)
}
.launchIn(viewModelScope)
.launchIn(modelScope)
}
private fun disableSortingByBalanceIfListChanged(dragOperationType: DragAndDropAdapter.DragOperation.Type) {
@ -210,7 +210,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
maybeAppCurrency.getOrElse { AppCurrency.Default }
}
.stateIn(
scope = viewModelScope,
scope = modelScope,
started = SharingStarted.Eagerly,
initialValue = AppCurrency.Default,
)

View file

@ -0,0 +1,84 @@
package com.tangem.feature.wallet.child.wallet
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.utils.findActivity
import com.tangem.feature.wallet.child.wallet.model.WalletModel
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.features.markets.entry.MarketsEntryComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
internal class WalletComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted navigate: (WalletRoute) -> Unit,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
private val model: WalletModel = getOrCreateModel()
init {
lifecycle.subscribe(model.screenLifecycleProvider)
componentScope.launch { model.innerWalletRouter.navigateToFlow.collect { navigate(it) } }
}
private val dialog = childSlot(
source = model.innerWalletRouter.dialogNavigation,
serializer = WalletDialogConfig.serializer(),
handleBackButton = true,
childFactory = { dialogConfig, componentContext ->
when (dialogConfig) {
is WalletDialogConfig.RenameWallet -> {
renameWalletComponentFactory.create(
context = childByContext(componentContext),
params = RenameWalletComponent.Params(
userWalletId = dialogConfig.userWalletId,
currentName = dialogConfig.currentName,
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
),
)
}
}
},
)
private val marketsEntryComponent = marketsEntryComponentFactory.create(child("marketsEntryComponent"))
@Composable
override fun Content(modifier: Modifier) {
val activity = LocalContext.current.findActivity()
BackHandler { activity.finish() }
val dialog by dialog.subscribeAsState()
WalletScreen(
state = model.uiState.collectAsStateWithLifecycle().value,
marketsEntryComponent = marketsEntryComponent,
)
dialog.child?.instance?.Dialog()
}
@AssistedFactory
interface Factory {
fun create(appComponentContext: AppComponentContext, navigate: (WalletRoute) -> Unit): WalletComponent
}
}

View file

@ -1,17 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels
package com.tangem.feature.wallet.child.wallet.model
import androidx.compose.runtime.Stable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.getOrElse
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
@ -29,13 +29,11 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenSta
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@ -45,8 +43,9 @@ import javax.inject.Inject
@Suppress("LongParameterList", "LargeClass")
@Stable
@HiltViewModel
internal class WalletViewModel @Inject constructor(
@ModelScoped
internal class WalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val stateHolder: WalletStateController,
private val clickIntents: WalletClickIntents,
private val walletEventSender: WalletEventSender,
@ -59,8 +58,6 @@ internal class WalletViewModel @Inject constructor(
private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val dispatchers: CoroutineDispatcherProvider,
private val screenLifecycleProvider: ScreenLifecycleProvider,
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
private val walletDeepLinksHandler: WalletDeepLinksHandler,
private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
@ -70,12 +67,13 @@ internal class WalletViewModel @Inject constructor(
private val tokenListStore: MultiWalletTokenListStore,
private val onrampStatusFactory: OnrampStatusFactory,
private val walletFeatureToggles: WalletFeatureToggles,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
analyticsEventsHandler: AnalyticsEventHandler,
) : ViewModel() {
) : Model() {
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
private lateinit var router: InnerWalletRouter
private val walletsUpdateJobHolder = JobHolder()
private val refreshWalletJobHolder = JobHolder()
private val expressStatusJobHolder = JobHolder()
@ -96,25 +94,18 @@ internal class WalletViewModel @Inject constructor(
subscribeToScreenBackgroundState()
subscribeOnPushNotificationsPermission()
subscribeOnExpressTransactionsUpdates()
clickIntents.initialize(innerWalletRouter, modelScope)
}
private fun maybeMigrateNames() {
viewModelScope.launch {
modelScope.launch {
walletNameMigrationUseCase()
}
}
fun setWalletRouter(router: InnerWalletRouter) {
this.router = router
clickIntents.initialize(router, viewModelScope)
}
fun subscribeToLifecycle(lifecycleOwner: LifecycleOwner) {
lifecycleOwner.lifecycle.addObserver(screenLifecycleProvider)
}
override fun onCleared() {
super.onCleared()
override fun onDestroy() {
super.onDestroy()
tokenListStore.clear()
stateHolder.clear()
@ -122,15 +113,15 @@ internal class WalletViewModel @Inject constructor(
}
private fun suggestToEnableBiometrics() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
if (isShowSaveWalletScreenEnabled()) router.openSaveUserWalletScreen()
if (isShowSaveWalletScreenEnabled()) innerWalletRouter.openSaveUserWalletScreen()
}
}
private fun suggestToOpenMarkets() {
viewModelScope.launch {
modelScope.launch {
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
if (shouldShowMarketsTooltipUseCase()) {
@ -144,7 +135,7 @@ internal class WalletViewModel @Inject constructor(
}
private suspend fun isShowSaveWalletScreenEnabled(): Boolean {
return router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()
return innerWalletRouter.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()
}
private fun subscribeToUserWalletsUpdates() {
@ -156,7 +147,7 @@ internal class WalletViewModel @Inject constructor(
}
.onEach(::updateWallets)
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
.saveIn(walletsUpdateJobHolder)
}
@ -168,11 +159,11 @@ internal class WalletViewModel @Inject constructor(
stateHolder.update(transformer = UpdateBalanceHidingModeTransformer(it.isBalanceHidden))
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
}
private fun subscribeOnPushNotificationsPermission() {
viewModelScope.launch {
modelScope.launch {
val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION)
val isPushPermissionAvailable = getPushPermissionOrNull() != null
if (!shouldRequestPush || !isPushPermissionAvailable) return@launch
@ -203,10 +194,10 @@ internal class WalletViewModel @Inject constructor(
selectedWalletAnalyticsSender.send(selectedWallet)
}
walletDeepLinksHandler.registerForWallet(viewModel = this, userWallet = selectedWallet)
walletDeepLinksHandler.registerForWallet(scope = modelScope, userWallet = selectedWallet)
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
}
}
@ -227,14 +218,14 @@ internal class WalletViewModel @Inject constructor(
!isBackground -> subscribeOnExpressTransactionsUpdates()
}
}
.launchIn(viewModelScope)
.launchIn(modelScope)
}
private fun subscribeOnExpressTransactionsUpdates() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
expressTxStatusTaskScheduler.cancelTask()
expressTxStatusTaskScheduler.scheduleTask(
viewModelScope,
modelScope,
PeriodicTask(
isDelayFirst = false,
delay = EXPRESS_STATUS_UPDATE_DELAY,
@ -249,7 +240,7 @@ internal class WalletViewModel @Inject constructor(
}
private fun needToRefreshTimer() {
viewModelScope.launch {
modelScope.launch {
delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS)
needToRefreshWallet = true
}.saveIn(refreshWalletJobHolder)
@ -259,7 +250,7 @@ internal class WalletViewModel @Inject constructor(
needToRefreshWallet = false
val state = stateHolder.uiState.value
val wallet = state.wallets.getOrNull(state.selectedWalletIndex) ?: return
viewModelScope.launch {
modelScope.launch {
refreshMultiCurrencyWalletQuotesUseCase(wallet.walletCardState.id).getOrElse {
Timber.e("Failed to refreshMultiCurrencyWalletQuotesUseCase $it")
}
@ -279,7 +270,7 @@ internal class WalletViewModel @Inject constructor(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
isRefresh = true,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
stateHolder.update(
@ -299,12 +290,6 @@ internal class WalletViewModel @Inject constructor(
}
private suspend fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) {
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = viewModelScope,
)
stateHolder.update(
transformer = InitializeWalletsTransformer(
selectedWalletIndex = action.selectedWalletIndex,
@ -315,6 +300,12 @@ internal class WalletViewModel @Inject constructor(
),
)
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = modelScope,
)
if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) {
withContext(dispatchers.io) { delay(timeMillis = 1_800) }
@ -337,7 +328,7 @@ internal class WalletViewModel @Inject constructor(
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
stateHolder.update(
@ -355,7 +346,7 @@ internal class WalletViewModel @Inject constructor(
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
stateHolder.update(
@ -381,7 +372,7 @@ internal class WalletViewModel @Inject constructor(
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
val newSelectedWalletIndex = if (action.selectedWalletIndex - action.deletedWalletIndex == 1) {
@ -439,7 +430,7 @@ internal class WalletViewModel @Inject constructor(
walletScreenContentLoader.load(
userWallet = action.selectedWallet,
clickIntents = clickIntents,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels
package com.tangem.feature.wallet.child.wallet.model
import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
@ -9,7 +10,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import dagger.hilt.android.scopes.ViewModelScoped
import timber.log.Timber
import javax.inject.Inject
@ -18,7 +18,7 @@ import javax.inject.Inject
*
* @property getSelectedWalletSyncUseCase use case that returns selected wallet
*/
@ViewModelScoped
@ModelScoped
internal class WalletsUpdateActionResolver @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
) {

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import kotlinx.coroutines.CoroutineScope
@ -14,13 +14,13 @@ import kotlin.properties.Delegates
internal abstract class BaseWalletClickIntents {
protected val router: InnerWalletRouter get() = _router
protected val viewModelScope: CoroutineScope get() = _viewModelScope
protected val modelScope: CoroutineScope get() = _modelScope
private var _router: InnerWalletRouter by Delegates.notNull()
private var _viewModelScope: CoroutineScope by Delegates.notNull()
private var _modelScope: CoroutineScope by Delegates.notNull()
open fun initialize(router: InnerWalletRouter, coroutineScope: CoroutineScope) {
_router = router
_viewModelScope = coroutineScope
_modelScope = coroutineScope
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
@ -15,7 +16,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.converte
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxDetailsBottomSheetConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.launch
import timber.log.Timber
@ -32,7 +32,7 @@ internal interface VisaWalletIntents {
fun onExploreClick(exploreUrl: String)
}
@ViewModelScoped
@ModelScoped
internal class VisaWalletIntentsImplementor @Inject constructor(
private val stateController: WalletStateController,
private val eventSender: WalletEventSender,
@ -49,7 +49,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
override fun onDepositClick() {
val userWalletId = stateController.getSelectedWalletId()
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val currencyStatus = getPrimaryCurrencyStatus(userWalletId) ?: return@launch
createReceiveBottomSheetContent(currencyStatus)?.let { content ->
@ -79,7 +79,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
}
override fun onBalancesAndLimitsClick() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val userWalletId = stateController.getSelectedWalletId()
val balancesAndLimits = getVisaCurrencyUseCase(userWalletId)
.getOrElse {
@ -104,7 +104,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor(
}
override fun onVisaTransactionClick(id: String) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val userWalletId = stateController.getSelectedWalletId()
val visaCurrency = getVisaCurrencyUseCase(userWalletId)
.getOrElse {

View file

@ -1,10 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.card.DeleteSavedAccessCodesUseCase
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.wallets.models.UserWalletId
@ -35,6 +36,7 @@ internal interface WalletCardClickIntents {
// TODO: Refactor
@Suppress("LongParameterList")
@ModelScoped
internal class WalletCardClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val tokenListStore: MultiWalletTokenListStore,
@ -74,7 +76,7 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
}
override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
walletScreenContentLoader.cancel(userWalletId)
tokenListStore.remove(userWalletId)

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.extenstions.unwrap
import com.tangem.domain.common.util.cardTypesResolver
@ -21,7 +22,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetRefre
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -29,7 +29,7 @@ import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@ViewModelScoped
@ModelScoped
internal class WalletClickIntents @Inject constructor(
private val walletCardClickIntentsImplementor: WalletCardClickIntentsImplementor,
private val warningsClickIntentsImplementer: WalletWarningsClickIntentsImplementor,
@ -75,7 +75,7 @@ internal class WalletClickIntents @Inject constructor(
return
}
viewModelScope.launch {
modelScope.launch {
launch { neverToShowWalletsScrollPreview() }
val maybeUserWallet = selectWalletUseCase(
@ -88,7 +88,7 @@ internal class WalletClickIntents @Inject constructor(
walletScreenContentLoader.load(
userWallet = it,
clickIntents = this@WalletClickIntents,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
}
}
@ -107,6 +107,7 @@ internal class WalletClickIntents @Inject constructor(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> Unit
}
}
@ -122,7 +123,7 @@ internal class WalletClickIntents @Inject constructor(
SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = showRefreshState),
)
viewModelScope.launch {
modelScope.launch {
val maybeFetchResult = if (userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) {
fetchCardTokenListUseCase(userWalletId = userWallet.walletId, refresh = true)
} else {
@ -167,14 +168,14 @@ internal class WalletClickIntents @Inject constructor(
SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = showRefreshState),
)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
fetchCurrencyStatusUseCase(userWallet.walletId, refresh = true)
walletScreenContentLoader.load(
userWallet = userWallet,
clickIntents = this@WalletClickIntents,
isRefresh = true,
coroutineScope = viewModelScope,
coroutineScope = modelScope,
)
stateHolder.update(

View file

@ -1,7 +1,8 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
@ -16,15 +17,11 @@ import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
@ -33,8 +30,6 @@ import javax.inject.Inject
internal interface WalletContentClickIntents {
fun onBackClick()
fun onDetailsClick()
fun onManageTokensClick()
@ -53,7 +48,7 @@ internal interface WalletContentClickIntents {
fun onDissmissBottomSheet()
fun onGoToProviderClick(externalTxId: String)
fun onGoToProviderClick(externalTxUrl: String)
fun onExpressTransactionClick(txId: String)
@ -63,7 +58,7 @@ internal interface WalletContentClickIntents {
}
@Suppress("LongParameterList")
@ViewModelScoped
@ModelScoped
internal class WalletContentClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor,
@ -79,10 +74,8 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
private val walletEventSender: WalletEventSender,
) : BaseWalletClickIntents(), WalletContentClickIntents {
override fun onBackClick() = router.popBackStack()
override fun onDetailsClick() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val userWalletId = stateHolder.getSelectedWalletId()
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
@ -124,7 +117,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onDismissMarketsOnboarding() {
stateHolder.update { it.copy(showMarketsOnboarding = false) }
viewModelScope.launch {
modelScope.launch {
shouldShowMarketsTooltipUseCase(isShown = true)
}
}
@ -134,7 +127,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
override fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val userWalletId = stateHolder.getSelectedWalletId()
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
Timber.e(
@ -169,7 +162,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
override fun onTransactionClick(txHash: String) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val currency = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(
userWalletId = stateHolder.getSelectedWalletId(),
)
@ -191,7 +184,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onDissmissBottomSheet() {
val userWalletId = stateHolder.getSelectedWalletId()
if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
onrampStatusFactory.removeTransactionOnBottomSheetClosed(forceDispose = false)
}
}
@ -199,7 +192,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
override fun onExpressTransactionClick(txId: String) {
viewModelScope.launch {
modelScope.launch {
val userWalletId = stateHolder.getSelectedWalletId()
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
?: return@launch
@ -236,7 +229,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onDisposeExpressStatus() {
val userWalletId = stateHolder.getSelectedWalletId()
if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
onrampStatusFactory.removeTransactionOnBottomSheetClosed(forceDispose = true)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.routing.AppRoute
@ -7,6 +7,7 @@ import com.tangem.common.ui.tokens.getUnavailabilityReasonText
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
@ -55,7 +56,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSend
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.swap.SwapFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
@ -101,7 +101,7 @@ interface WalletCurrencyActionsClickIntents {
}
@Suppress("LongParameterList", "LargeClass")
@ViewModelScoped
@ModelScoped
internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val walletEventSender: WalletEventSender,
@ -205,7 +205,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
event = TokenScreenAnalyticsEvent.ButtonCopyAddress(cryptoCurrencyStatus.currency.symbol),
)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
walletManagersFacade.getDefaultAddress(
userWalletId = stateHolder.getSelectedWalletId(),
network = cryptoCurrencyStatus.currency.network,
@ -223,7 +223,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
event = TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrencyStatus.currency.symbol),
)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = getHideTokeAlertConfig(stateHolder.getSelectedWalletId(), cryptoCurrencyStatus),
@ -270,7 +270,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
override fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus) {
val userWalletId = stateHolder.getSelectedWalletId()
viewModelScope.launch(dispatchers.io) {
modelScope.launch(dispatchers.io) {
removeCurrencyUseCase(userWalletId, cryptoCurrencyStatus.currency)
.fold(
ifLeft = {
@ -296,7 +296,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
if (handleUnavailabilityReason(unavailabilityReason)) return
showErrorIfDemoModeOrElse {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
action = TradeCryptoAction.Sell(
cryptoCurrencyStatus = cryptoCurrencyStatus,
@ -329,7 +329,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
)
} else {
showErrorIfDemoModeOrElse {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
TradeCryptoAction.Buy(
userWallet = userWallet,
@ -368,7 +368,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
override fun onAnalyticsClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
viewModelScope.launch {
modelScope.launch {
val rawId = cryptoCurrencyStatus.currency.id.rawCurrencyId ?: return@launch
val tokenMarketParams = TokenMarketParams(
@ -401,7 +401,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
viewModelScope.launch {
modelScope.launch {
val userWalletId = stateHolder.getSelectedWalletId()
val cryptoCurrency = cryptoCurrencyStatus.currency
@ -436,7 +436,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
return
}
viewModelScope.launch {
modelScope.launch {
val swapRoute = getSwapRoute(
AppRoute.SwapCrypto(userWalletId = userWalletId),
)
@ -463,7 +463,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun openExplorer() {
val userWalletId = stateHolder.getSelectedWalletId()
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val currencyStatus = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId) ?: return@launch
when (val addresses = currencyStatus.value.networkAddress) {
@ -509,7 +509,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
currency: CryptoCurrency,
addressModel: AddressModel,
) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
@ -543,7 +543,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.DefaultAlert(
@ -563,7 +563,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
route: AppRoute,
eventCreator: (AnalyticsParam.Status) -> MainScreenAnalyticsEvent,
) {
viewModelScope.launch {
modelScope.launch {
statusFlow.foldStatus(
onContent = { handleContent(route, eventCreator) },
onError = { handleError(eventCreator = eventCreator) },

View file

@ -1,11 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.settings.NeverRequestPermissionUseCase
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -20,7 +20,7 @@ internal interface WalletPushPermissionClickIntents {
fun onAllowPushPermission()
}
@ViewModelScoped
@ModelScoped
internal class WalletPushPermissionClickIntentsImplementor @Inject constructor(
private val neverRequestPermissionUseCase: NeverRequestPermissionUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
@ -36,7 +36,7 @@ internal class WalletPushPermissionClickIntentsImplementor @Inject constructor(
override fun onNeverAskPushPermission(isUserDismissed: Boolean) {
if (isUserDismissedDialog != isUserDismissed) return
viewModelScope.launch {
modelScope.launch {
analyticsEventHandler.send(
PushNotificationAnalyticEvents.ButtonLater(AnalyticsParam.ScreensSources.Main),
)
@ -46,14 +46,14 @@ internal class WalletPushPermissionClickIntentsImplementor @Inject constructor(
override fun onDenyPushPermission() {
analyticsEventHandler.send(PushNotificationAnalyticEvents.PermissionStatus(isAllowed = false))
viewModelScope.launch {
modelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION)
}
}
override fun onAllowPushPermission() {
analyticsEventHandler.send(PushNotificationAnalyticEvents.PermissionStatus(isAllowed = true))
viewModelScope.launch {
modelScope.launch {
neverRequestPermissionUseCase(PUSH_PERMISSION)
}
}

View file

@ -1,9 +1,10 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.common.TangemBlogUrlBuilder
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.card.DerivePublicKeysUseCase
@ -38,7 +39,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ -55,6 +55,8 @@ internal interface WalletWarningsClickIntents {
fun onUnlockWalletClick()
fun onUnlockVisaAccessClick()
fun onScanToUnlockWalletClick()
fun onLikeAppClick()
@ -79,7 +81,7 @@ internal interface WalletWarningsClickIntents {
}
@Suppress("LongParameterList")
@ViewModelScoped
@ModelScoped
internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
private val walletEventSender: WalletEventSender,
@ -108,7 +110,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
private fun prepareAndStartOnboardingProcess() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
getSelectedUserWallet()?.let {
reduxStateHolder.dispatch(
LegacyAction.StartOnboardingProcess(
@ -127,7 +129,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
override fun onCloseAlreadySignedHashesWarningClick() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val userWallet = getSelectedUserWallet() ?: return@launch
setCardWasScannedUseCase(cardId = userWallet.cardId)
@ -138,7 +140,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(Basic.CardWasScanned(AnalyticsParam.ScreensSources.Main))
analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped)
viewModelScope.launch {
modelScope.launch {
val userWallet = getSelectedUserWallet() ?: return@launch
derivePublicKeysUseCase(
@ -173,13 +175,17 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onUnlockWalletClick() {
analyticsEventHandler.send(MainScreen.UnlockAllWithBiometrics)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
unlockWalletsUseCase(type = UnlockType.ALL_WITHOUT_SELECT)
.onRight { stateHolder.update(CloseBottomSheetTransformer(stateHolder.getSelectedWalletId())) }
.onLeft(::handleUnlockWalletsError)
}
}
override fun onUnlockVisaAccessClick() {
openScanCardDialog()
}
private fun handleUnlockWalletsError(error: UnlockWalletsError) {
val event = when (error) {
is UnlockWalletsError.DataError,
@ -199,7 +205,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
private fun openScanCardDialog() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
scanCardToUnlockWalletClickHandler(walletId = stateHolder.getSelectedWalletId())
.onLeft { error ->
when (error) {
@ -220,7 +226,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
walletEventSender.send(
event = WalletEvent.RateApp(
onDismissClick = {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
neverToSuggestRateAppUseCase()
}
},
@ -231,7 +237,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onDislikeAppClick() {
analyticsEventHandler.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Disliked))
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
neverToSuggestRateAppUseCase()
val scanResponse = getSelectedUserWallet()?.scanResponse ?: return@launch
@ -244,7 +250,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
override fun onCloseRateAppWarningClick() {
analyticsEventHandler.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Closed))
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
remindToRateAppLaterUseCase()
}
}
@ -257,7 +263,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
action = TokenSwapPromoAnalyticsEvent.PromotionBannerClicked.BannerAction.Closed,
),
)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoWalletUseCase.neverToShow()
}
}
@ -266,14 +272,14 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
val scanResponse = getSelectedUserWallet()?.scanResponse ?: return
val cardInfo = getCardInfoUseCase(scanResponse).getOrNull() ?: return
viewModelScope.launch {
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.DirectUserRequest(cardInfo = cardInfo))
}
}
override fun onNoteMigrationButtonClick(url: String) {
analyticsEventHandler.send(MainScreen.NotePromoButton)
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
router.openUrl(url)
}
}
@ -288,7 +294,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
onOkClick = {
viewModelScope.launch {
modelScope.launch {
seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId)
urlOpener.openUrl(
@ -311,7 +317,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_no),
onOkClick = {
viewModelScope.launch {
modelScope.launch {
seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId)
}
},
@ -330,7 +336,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
state = WalletAlertState.SimpleOkAlert(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
onOkClick = {
viewModelScope.launch {
modelScope.launch {
seedPhraseNotificationUseCase.acceptSecond(userWalletId = userWallet.walletId)
urlOpener.openUrl(
@ -348,7 +354,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonDeclined)
viewModelScope.launch {
modelScope.launch {
seedPhraseNotificationUseCase.rejectSecond(userWalletId = userWallet.walletId)
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.feature.wallet.di
import com.tangem.core.decompose.model.Model
import com.tangem.feature.wallet.DefaultWalletEntryComponent
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModel
import com.tangem.feature.wallet.child.wallet.model.WalletModel
import com.tangem.features.wallet.WalletEntryComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(SingletonComponent::class)
internal interface WalletFeatureModule {
@Binds
fun bindComponentFactory(impl: DefaultWalletEntryComponent.Factory): WalletEntryComponent.Factory
@Binds
@IntoMap
@ClassKey(WalletModel::class)
fun bindWalletModel(model: WalletModel): Model
@Binds
@IntoMap
@ClassKey(OrganizeTokensModel::class)
fun bindOrganizeTokensModel(model: OrganizeTokensModel): Model
}

View file

@ -1,18 +1,18 @@
package com.tangem.feature.wallet.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter
import com.tangem.features.wallet.navigation.WalletRouter
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.scopes.ActivityScoped
@Module
@InstallIn(ActivityComponent::class)
@InstallIn(ModelComponent::class)
internal interface WalletRouterModule {
@Binds
@ActivityScoped
fun bindsWalletRouter(defaultWalletRouter: DefaultWalletRouter): WalletRouter
@ModelScoped
fun bindsWalletRouter(defaultWalletRouter: DefaultWalletRouter): InnerWalletRouter
}

View file

@ -0,0 +1,14 @@
package com.tangem.feature.wallet.navigation
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.serialization.Serializable
@Serializable
internal sealed class WalletRoute {
@Serializable
data object Wallet : WalletRoute()
@Serializable
data class OrganizeTokens(val userWalletId: UserWalletId) : WalletRoute()
}

View file

@ -1,85 +0,0 @@
package com.tangem.feature.wallet.presentation
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.defaultComponentContext
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.utils.asRouter
import com.tangem.core.decompose.context.DefaultAppComponentContext
import com.tangem.core.decompose.di.DecomposeComponent
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.features.wallet.navigation.WalletRouter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/**
* Wallet fragment
*
[REDACTED_AUTHOR]
*/
@AndroidEntryPoint
internal class WalletFragment : ComposeFragment() {
@Inject
override lateinit var uiDependencies: UiDependencies
/** Feature router */
@Inject
internal lateinit var walletRouter: WalletRouter
@Inject
internal lateinit var coroutineDispatcherProvider: CoroutineDispatcherProvider
@Inject
internal lateinit var componentBuilder: DecomposeComponent.Builder
@Inject
internal lateinit var appRouter: AppRouter
@Inject
@GlobalUiMessageSender
internal lateinit var messageSender: UiMessageSender
private val _walletRouter: InnerWalletRouter
get() = requireNotNull(walletRouter as? InnerWalletRouter) {
"_walletRouter should be instance of InnerWalletRouter"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
_walletRouter.initializeResources(
appComponentContext = DefaultAppComponentContext(
componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher),
messageSender = messageSender,
dispatchers = coroutineDispatcherProvider,
hiltComponentBuilder = componentBuilder,
replaceRouter = appRouter.asRouter(),
),
)
}
@Composable
override fun ScreenContent(modifier: Modifier) {
_walletRouter.Initialize(
onFinish = remember(requireActivity()) {
{
requireActivity().finish()
}
},
)
}
companion object {
/** Create wallet fragment instance */
fun create(): WalletFragment = WalletFragment()
}
}

View file

@ -8,11 +8,16 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState
import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import java.util.UUID
@ -32,8 +37,7 @@ internal object WalletPreviewData {
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _ -> },
onDeleteClick = {},
dropDownItems = persistentListOf(),
cardCount = 1,
isZeroBalance = false,
isBalanceFlickering = false,
@ -45,8 +49,7 @@ internal object WalletPreviewData {
id = UserWalletId("321"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _ -> },
onDeleteClick = {},
dropDownItems = persistentListOf(),
)
}
@ -55,8 +58,7 @@ internal object WalletPreviewData {
id = UserWalletId("24"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
onRenameClick = { _ -> },
onDeleteClick = {},
dropDownItems = persistentListOf(),
)
}

View file

@ -9,6 +9,7 @@ import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.*
@ -87,8 +88,7 @@ internal object WalletScreenPreviewData {
content = TextReference.Str("Locked"),
),
imageResId = R.drawable.ill_note_btc_120_106,
onRenameClick = { _ -> },
onDeleteClick = {},
dropDownItems = persistentListOf(),
)
}
private val miltiUnreachableCard by lazy {
@ -102,8 +102,7 @@ internal object WalletScreenPreviewData {
imageResId = R.drawable.ill_wallet2_cards3_120_106,
cardCount = 3,
balance = DASH_SIGN,
onRenameClick = { _ -> },
onDeleteClick = {},
dropDownItems = persistentListOf(),
isZeroBalance = false,
isBalanceFlickering = false,
)
@ -146,7 +145,6 @@ internal object WalletScreenPreviewData {
)
internal val walletScreenState = WalletScreenState(
onBackClick = {},
topBarConfig = topBarConfig,
selectedWalletIndex = 0,
wallets = persistentListOf(

View file

@ -1,7 +1,5 @@
package com.tangem.feature.wallet.presentation.deeplink
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -13,8 +11,9 @@ import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.utils.coroutines.launchOnCancellation
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
@ -32,14 +31,14 @@ internal class WalletDeepLinksHandler @Inject constructor(
private var deepLinksMap = mutableMapOf<UserWalletId, List<DeepLink>>()
fun registerForWallet(viewModel: ViewModel, userWallet: UserWallet) {
fun registerForWallet(scope: CoroutineScope, userWallet: UserWallet) {
val deepLinks = deepLinksMap.getOrPut(userWallet.walletId) {
getDeepLinks(userWallet, viewModel.viewModelScope)
getDeepLinks(userWallet, scope)
}
deepLinksRegistry.unregisterByIds(deepLinks.map { it.id })
deepLinksRegistry.register(deepLinks = deepLinks)
viewModel.addCloseable {
scope.launchOnCancellation {
deepLinksRegistry.unregister(deepLinks)
}
}

View file

@ -12,8 +12,10 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.orZero
import java.math.BigDecimal
internal class CryptoCurrencyToDraggableItemConverter(
@ -63,7 +65,11 @@ internal class CryptoCurrencyToDraggableItemConverter(
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance()?.multiply(fiatRate) ?: BigDecimal.ZERO
val fiatYieldBalance = if (BlockchainUtils.isIncludeStakingTotalBalance(currency.currency.network.id.value)) {
yieldBalance?.getTotalWithRewardsStakingBalance()?.multiply(fiatRate).orZero()
} else {
BigDecimal.ZERO
}
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }

View file

@ -1,144 +1,41 @@
package com.tangem.feature.wallet.presentation.router
import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.fragment.app.Fragment
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
import com.arkivanov.decompose.router.slot.ChildSlot
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.childSlot
import com.arkivanov.decompose.router.slot.dismiss
import com.arkivanov.decompose.value.Value
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRoute.ManageTokens.Source
import com.tangem.common.routing.AppRouter
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.decompose.ComposableDialogComponent
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.redux.StateDialog
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.WalletFragment
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
import com.tangem.feature.walletsettings.component.RenameWalletComponent
import com.tangem.features.markets.entry.MarketsEntryComponent
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import javax.inject.Inject
import kotlin.properties.Delegates
/** Default implementation of wallet feature router */
@ModelScoped
internal class DefaultWalletRouter @Inject constructor(
private val router: AppRouter,
private val urlOpener: UrlOpener,
private val reduxStateHolder: ReduxStateHolder,
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
private val marketsEntryComponentFactory: MarketsEntryComponent.Factory,
private val renameWalletComponentFactory: RenameWalletComponent.Factory,
) : InnerWalletRouter {
private var navController: NavHostController by Delegates.notNull()
private var onFinish: () -> Unit = {}
private lateinit var marketsEntryComponent: MarketsEntryComponent
private lateinit var dialog: Value<ChildSlot<WalletDialogConfig, ComposableDialogComponent>>
override val dialogNavigation: SlotNavigation<WalletDialogConfig> = SlotNavigation()
override fun initializeResources(appComponentContext: AppComponentContext) {
marketsEntryComponent = marketsEntryComponentFactory.create(appComponentContext)
dialog = appComponentContext.childSlot(
source = dialogNavigation,
serializer = WalletDialogConfig.serializer(),
handleBackButton = true,
childFactory = { dialogConfig, componentContext ->
dialogChild(
appContext = appComponentContext,
dialogConfig = dialogConfig,
componentContext = componentContext,
)
},
)
}
override fun getEntryFragment(): Fragment = WalletFragment.create()
@Composable
override fun Initialize(onFinish: () -> Unit) {
this.onFinish = onFinish
NavHost(
navController = rememberNavController().apply { navController = this },
startDestination = WalletRoute.Wallet.route,
) {
composable(WalletRoute.Wallet.route) {
val viewModel = hiltViewModel<WalletViewModel>().apply {
setWalletRouter(router = this@DefaultWalletRouter)
subscribeToLifecycle(LocalLifecycleOwner.current)
}
val dialog by dialog.subscribeAsState()
WalletScreen(
state = viewModel.uiState.collectAsStateWithLifecycle().value,
marketsEntryComponent = marketsEntryComponent,
)
dialog.child?.instance?.Dialog()
}
composable(
WalletRoute.OrganizeTokens.route,
arguments = listOf(navArgument(WalletRoute.userWalletIdKey) { type = NavType.StringType }),
) {
val viewModel = hiltViewModel<OrganizeTokensViewModel>().apply {
router = this@DefaultWalletRouter
}
LocalLifecycleOwner.current.lifecycle.addObserver(viewModel)
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
OrganizeTokensScreen(
state = uiState,
)
}
}
}
@SuppressLint("RestrictedApi")
override fun popBackStack() {
/*
* It's hack that avoid issue with closing the wallet screen.
* We are using NavGraph only inside feature so first backstack's element is entry of NavGraph and
* next element is wallet screen entry.
* If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment.
*/
if (navController.currentBackStack.value.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) {
onFinish.invoke()
} else {
navController.popBackStack()
}
}
override val navigateToFlow = MutableSharedFlow<WalletRoute>(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_LATEST,
)
override fun openOrganizeTokensScreen(userWalletId: UserWalletId) {
navController.navigate(WalletRoute.OrganizeTokens.createRoute(userWalletId))
navigateToFlow.tryEmit(WalletRoute.OrganizeTokens(userWalletId))
}
override fun openDetailsScreen(selectedWalletId: UserWalletId) {
@ -215,25 +112,4 @@ internal class DefaultWalletRouter @Inject constructor(
override fun openScanFailedDialog(onTryAgain: () -> Unit) {
reduxStateHolder.dispatchDialogShow(StateDialog.ScanFailsDialog(StateDialog.ScanFailsSource.MAIN, onTryAgain))
}
private fun dialogChild(
appContext: AppComponentContext,
dialogConfig: WalletDialogConfig,
componentContext: ComponentContext,
): ComposableDialogComponent = when (dialogConfig) {
is WalletDialogConfig.RenameWallet -> {
renameWalletComponentFactory.create(
context = appContext.childByContext(componentContext),
params = RenameWalletComponent.Params(
userWalletId = dialogConfig.userWalletId,
currentName = dialogConfig.currentName,
onDismiss = dialogNavigation::dismiss,
),
)
}
}
private companion object {
const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2
}
}

View file

@ -1,14 +1,13 @@
package com.tangem.feature.wallet.presentation.router
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.navigation.WalletRoute
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import com.tangem.features.wallet.navigation.WalletRouter
import kotlinx.coroutines.flow.SharedFlow
/**
* Interface of inner wallet feature router
@ -19,22 +18,11 @@ import com.tangem.features.wallet.navigation.WalletRouter
[REDACTED_AUTHOR]
*/
@Stable
internal interface InnerWalletRouter : WalletRouter {
internal interface InnerWalletRouter {
val dialogNavigation: SlotNavigation<WalletDialogConfig>
fun initializeResources(appComponentContext: AppComponentContext)
/**
* Initialize router
*
* @param onFinish finish activity callback
*/
@Composable
fun Initialize(onFinish: () -> Unit)
/** Pop back stack */
fun popBackStack()
val navigateToFlow: SharedFlow<WalletRoute>
/** Open organize tokens screen */
fun openOrganizeTokensScreen(userWalletId: UserWalletId)

View file

@ -1,24 +0,0 @@
package com.tangem.feature.wallet.presentation.router
import com.tangem.domain.wallets.models.UserWalletId
/**
* Wallet feature screens
*
* @property route route string representation
*
[REDACTED_AUTHOR]
*/
internal sealed class WalletRoute(val route: String) {
object Wallet : WalletRoute(route = "wallet")
object OrganizeTokens : WalletRoute(route = "wallet/{$userWalletIdKey}/organize_tokens") {
fun createRoute(userWalletId: UserWalletId) = "wallet/${userWalletId.stringValue}/organize_tokens"
}
companion object {
const val userWalletIdKey = "userWalletId"
}
}

View file

@ -8,6 +8,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.extensions.isZero
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase
import com.tangem.domain.analytics.model.WalletBalanceState
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -19,13 +20,12 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnaly
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.math.BigDecimal
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class TokenListAnalyticsSender @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val checkIsWalletToppedUpUseCase: CheckIsWalletToppedUpUseCase,

View file

@ -3,16 +3,16 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.TokenSwapPromoAnalyticsEvent.ProgramName
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class WalletWarningsAnalyticsSender @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val screenLifecycleProvider: ScreenLifecycleProvider,
@ -59,6 +59,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.Warning.SomeNetworksUnreachable,
is WalletNotification.Warning.NetworksUnreachable,
is WalletNotification.UsedOutdatedData,
is WalletNotification.UnlockVisaAccess,
-> null
is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport
is WalletNotification.Critical.SeedPhraseSecondNotification -> MainScreen.NoticeSeedPhraseSupportSecond

View file

@ -1,14 +1,14 @@
package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class WalletWarningsSingleEventSender @Inject constructor(
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val screenLifecycleProvider: ScreenLifecycleProvider,

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.core.lce.Lce
@ -16,8 +17,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import dagger.hilt.android.scopes.ViewModelScoped
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
@ -26,7 +26,7 @@ import javax.inject.Inject
import kotlin.collections.count
@Suppress("LongParameterList")
@ViewModelScoped
@ModelScoped
internal class GetMultiWalletWarningsFactory @Inject constructor(
private val tokenListStore: MultiWalletTokenListStore,
private val isDemoCardUseCase: IsDemoCardUseCase,

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import arrow.core.Either
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
@ -13,14 +14,13 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import dagger.hilt.android.scopes.ViewModelScoped
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class GetSingleWalletWarningsFactory @Inject constructor(
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,

View file

@ -1,17 +1,17 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.card.repository.CardRepository
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.DemoConfig
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWallet
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
class HasSingleWalletSignedHashesUseCase @Inject constructor(
private val cardRepository: CardRepository,
private val walletManagersFacade: WalletManagersFacade,

View file

@ -1,11 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWalletId
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.SharingStarted
@ -14,7 +14,7 @@ import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class MultiWalletTokenListStore @Inject constructor(
private val getTokenListUseCase: GetTokenListUseCase,
) {

View file

@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus
import com.tangem.domain.onramp.GetOnrampStatusUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
@ -13,14 +14,13 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class OnrampStatusFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,

View file

@ -1,13 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.loaders
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.*
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class WalletContentLoaderFactory @Inject constructor(
private val multiWalletContentLoaderFactory: MultiWalletContentLoaderFactory,
private val singleWalletWithTokenContentLoaderFactory: SingleWalletWithTokenContentLoaderFactory,

View file

@ -1,10 +1,10 @@
package com.tangem.feature.wallet.presentation.wallet.loaders
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.coroutines.CoroutineScope
import timber.log.Timber
import javax.inject.Inject
@ -18,7 +18,7 @@ import javax.inject.Inject
*
[REDACTED_AUTHOR]
*/
@ViewModelScoped
@ModelScoped
internal class WalletScreenContentLoader @Inject constructor(
private val factory: WalletContentLoaderFactory,
private val storage: WalletLoaderStorage,

View file

@ -1,11 +1,14 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -13,14 +16,15 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.swap.SwapFeatureToggles
@Suppress("LongParameterList")
@ModelScoped
internal class MultiWalletContentLoader(
private val userWallet: UserWallet,
private val stateHolder: WalletStateController,
@ -34,6 +38,7 @@ internal class MultiWalletContentLoader(
private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
@ -68,6 +73,11 @@ internal class MultiWalletContentLoader(
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
}
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
).let(::add)
}
}
}

View file

@ -1,11 +1,14 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -13,13 +16,11 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.features.swap.SwapFeatureToggles
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@Suppress("LongParameterList")
@ViewModelScoped
@ModelScoped
internal class MultiWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
@ -31,6 +32,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
@ -51,6 +53,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
getStoryContentUseCase = getStoryContentUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
swapFeatureToggles = swapFeatureToggles,
deepLinksRegistry = deepLinksRegistry,
)

View file

@ -10,11 +10,18 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.subscribers.PrimaryCurrencySubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletButtonsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletNotificationsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.TxHistorySubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletDropDownItemsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
@Suppress("LongParameterList")
internal class SingleWalletContentLoader(
@ -31,6 +38,7 @@ internal class SingleWalletContentLoader(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
) : WalletContentLoader(id = userWallet.walletId) {
@ -59,6 +67,11 @@ internal class SingleWalletContentLoader(
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
),
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
),
SingleWalletExpressStatusesSubscriber(
userWallet = userWallet,
stateHolder = stateHolder,

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
@ -10,14 +11,14 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import dagger.hilt.android.scopes.ViewModelScoped
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
@Suppress("LongParameterList")
internal class SingleWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
@ -30,6 +31,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
) {
@ -51,6 +53,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
)
}
}

View file

@ -5,6 +5,8 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -12,11 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletActionButtonsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenListSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.subscribers.*
import com.tangem.features.swap.SwapFeatureToggles
@Suppress("LongParameterList")
@ -32,6 +30,7 @@ internal class SingleWalletWithTokenContentLoader(
private val tokenListStore: MultiWalletTokenListStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
@ -65,6 +64,11 @@ internal class SingleWalletWithTokenContentLoader(
getStoryContentUseCase = getStoryContentUseCase,
).let(::add)
}
WalletDropDownItemsSubscriber(
stateHolder = stateHolder,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
clickIntents = clickIntents,
).let(::add)
}
}
}

View file

@ -1,10 +1,12 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.promo.GetStoryContentUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
@ -12,12 +14,13 @@ import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarnin
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.swap.SwapFeatureToggles
import javax.inject.Inject
// TODO: Refactor
@Suppress("LongParameterList")
@ModelScoped
internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
@ -28,6 +31,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWarningsSingleEventSender: WalletWarningsSingleEventSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val getStoryContentUseCase: GetStoryContentUseCase,
private val swapFeatureToggles: SwapFeatureToggles,
private val deepLinksRegistry: DeepLinksRegistry,
@ -47,6 +51,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
walletWarningsSingleEventSender = walletWarningsSingleEventSender,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
getStoryContentUseCase = getStoryContentUseCase,
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
swapFeatureToggles = swapFeatureToggles,
deepLinksRegistry = deepLinksRegistry,
)

View file

@ -6,7 +6,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
internal class VisaWalletContentLoader(
private val userWallet: UserWallet,

View file

@ -1,14 +1,14 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.visa.GetVisaCurrencyUseCase
import com.tangem.domain.visa.GetVisaTxHistoryUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@ViewModelScoped
@ModelScoped
internal class VisaWalletContentLoaderFactory @Inject constructor(
private val stateHolder: WalletStateController,
private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase,

View file

@ -87,7 +87,6 @@ internal class WalletStateController @Inject constructor() {
private fun getInitialState(): WalletScreenState {
return WalletScreenState(
onBackClick = {},
topBarConfig = WalletTopBarConfig(onDetailsClick = {}),
selectedWalletIndex = NOT_INITIALIZED_WALLET_INDEX,
wallets = persistentListOf(),

View file

@ -12,7 +12,6 @@ internal data class BalancesAndLimitsBottomSheetConfig(
val availableBalance: String,
val blockedBalance: String,
val debit: String,
val pending: String,
val amlVerified: String,
val onInfoClick: () -> Unit,
)

View file

@ -5,6 +5,7 @@ import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.ImmutableList
/** Wallet card state */
@Immutable
@ -23,11 +24,8 @@ internal sealed interface WalletCardState {
@get:DrawableRes
val imageResId: Int?
/** Lambda be invoked when Rename button is clicked */
val onRenameClick: (UserWalletId) -> Unit
/** Lambda be invoked when Delete button is clicked */
val onDeleteClick: (UserWalletId) -> Unit
/** Wallet drop down items */
val dropDownItems: ImmutableList<WalletDropDownItems>
/**
* Wallet card content state
@ -35,8 +33,7 @@ internal sealed interface WalletCardState {
* @property id wallet id
* @property title wallet name
* @property imageResId wallet image resource id
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property dropDownItems wallet dropdown items
* @property additionalInfo wallet additional info
* @property cardCount number of cards in the wallet
* @property balance wallet balance
@ -46,8 +43,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
override val dropDownItems: ImmutableList<WalletDropDownItems>,
val isBalanceFlickering: Boolean,
val cardCount: Int?,
val balance: String,
@ -61,16 +57,14 @@ internal sealed interface WalletCardState {
* @property title wallet name
* @property additionalInfo wallet additional info
* @property imageResId wallet image resource id
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property dropDownItems wallet dropdown items
*/
data class LockedContent(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
override val dropDownItems: ImmutableList<WalletDropDownItems>,
) : WalletCardState
/**
@ -79,16 +73,14 @@ internal sealed interface WalletCardState {
* @property id wallet id
* @property title wallet name
* @property imageResId wallet image resource id
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property dropDownItems wallet dropdown items
*/
data class Error(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = defaultAdditionalInfo,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
override val dropDownItems: ImmutableList<WalletDropDownItems>,
) : WalletCardState {
private companion object {
@ -103,24 +95,25 @@ internal sealed interface WalletCardState {
* @property id wallet id
* @property title wallet name
* @property imageResId wallet image resource id
* @property onRenameClick lambda be invoked when Rename button is clicked
* @property onDeleteClick lambda be invoked when Delete button is clicked
* @property dropDownItems wallet dropdown items
*/
data class Loading(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = null,
override val imageResId: Int?,
override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
override val dropDownItems: ImmutableList<WalletDropDownItems>,
) : WalletCardState
fun copySealed(title: String = this.title): WalletCardState {
fun copySealed(
title: String = this.title,
dropDownItems: ImmutableList<WalletDropDownItems> = this.dropDownItems,
): WalletCardState {
return when (this) {
is Content -> copy(title = title)
is Error -> copy(title = title)
is Loading -> copy(title = title)
is LockedContent -> copy(title = title)
is Content -> copy(title = title, dropDownItems = dropDownItems)
is Error -> copy(title = title, dropDownItems = dropDownItems)
is Loading -> copy(title = title, dropDownItems = dropDownItems)
is LockedContent -> copy(title = title, dropDownItems = dropDownItems)
}
}

View file

@ -0,0 +1,10 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.annotation.DrawableRes
import com.tangem.core.ui.extensions.TextReference
internal data class WalletDropDownItems(
val text: TextReference,
@DrawableRes val icon: Int,
val onClick: () -> Unit,
)

View file

@ -191,6 +191,19 @@ sealed class WalletNotification(val config: NotificationConfig) {
),
)
data class UnlockVisaAccess(val onUnlockClick: () -> Unit) : WalletNotification(
config = NotificationConfig(
title = resourceReference(id = R.string.visa_unlock_notification_title),
subtitle = resourceReference(id = R.string.visa_unlock_notification_subtitle),
iconResId = R.drawable.ic_locked_24,
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(id = R.string.visa_unlock_notification_button),
iconResId = R.drawable.ic_tangem_24,
onClick = onUnlockClick,
),
),
)
data class RateApp(
val onLikeClick: () -> Unit,
val onDislikeClick: () -> Unit,

View file

@ -6,7 +6,6 @@ import kotlinx.collections.immutable.ImmutableList
@Immutable
internal data class WalletScreenState(
val onBackClick: () -> Unit,
val topBarConfig: WalletTopBarConfig,
val selectedWalletIndex: Int,
val wallets: ImmutableList<WalletState>,

View file

@ -12,6 +12,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistor
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.WalletStateHolder
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
internal const val NOT_INITIALIZED_WALLET_INDEX = -1
@ -115,5 +116,27 @@ internal sealed interface WalletState : WalletStateHolder {
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null
}
data class AccessTokenLocked(
override val walletCardState: WalletCardState,
override val buttons: PersistentList<WalletManageButton>,
override val bottomSheetConfig: TangemBottomSheetConfig?,
val onExploreClick: () -> Unit,
val onUnlockVisaAccessNotificationClick: () -> Unit,
) : Visa(),
TxHistoryStateHolder by LockedTxHistoryStateHolder(onExploreClick),
WalletStateHolder by LockedWalletStateHolder(
walletCardState = walletCardState,
buttons = buttons,
bottomSheetConfig = bottomSheetConfig,
onUnlockNotificationClick = {},
) {
override val warnings: ImmutableList<WalletNotification> = persistentListOf(
WalletNotification.UnlockVisaAccess(onUnlockClick = onUnlockVisaAccessNotificationClick),
)
override val balancesAndLimitBlockState: BalancesAndLimitsBlockState? = null
}
}
}

View file

@ -4,7 +4,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList

View file

@ -25,6 +25,9 @@ internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletS
is WalletState.Visa.Locked -> prevState.copy(
bottomSheetConfig = updateConfig(prevState),
)
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
bottomSheetConfig = updateConfig(prevState),
)
}
}

View file

@ -7,7 +7,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.state.utils.createStateByWalletType
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
@ -31,7 +31,6 @@ internal class InitializeWalletsTransformer(
override fun transform(prevState: WalletScreenState): WalletScreenState {
return prevState.copy(
onBackClick = clickIntents::onBackClick,
topBarConfig = createTopBarConfig(),
selectedWalletIndex = selectedWalletIndex,
wallets = wallets
@ -91,8 +90,7 @@ internal class InitializeWalletsTransformer(
title = name,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
imageResId = walletImageResolver.resolve(userWallet = this),
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
dropDownItems = persistentListOf(),
)
}

View file

@ -31,6 +31,9 @@ internal class OpenBottomSheetTransformer(
is WalletState.Visa.Locked -> prevState.copy(
bottomSheetConfig = updateConfig(),
)
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
bottomSheetConfig = updateConfig(),
)
}
}

View file

@ -5,7 +5,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList

View file

@ -44,6 +44,7 @@ internal class RenameWalletsTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.e("Impossible to rename wallet in locked state")
prevState

View file

@ -7,13 +7,14 @@ import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.extensions.isZero
import org.joda.time.DateTime
import org.joda.time.Days
@ -29,6 +30,10 @@ internal class SetBalancesAndLimitsTransformer(
override fun transformTyped(prevState: WalletState.Visa.Content): WalletState {
val visaCurrency = maybeVisaCurrency.getOrElse {
if (it is RefreshTokenExpiredException) {
return getRefreshTokenExpiredState(prevState)
}
return prevState.copy(
walletCardState = getErrorWalletCardState(prevState.walletCardState),
depositButtonState = prevState.depositButtonState.copy(isEnabled = false),
@ -58,8 +63,7 @@ internal class SetBalancesAndLimitsTransformer(
id = id,
title = title,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
)
}
}
@ -71,8 +75,7 @@ internal class SetBalancesAndLimitsTransformer(
title = title,
additionalInfo = createAdditionalInfo(visaCurrency),
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
balance = visaCurrency.balances.available.format {
crypto(visaCurrency.symbol, visaCurrency.decimals)
},
@ -102,4 +105,14 @@ internal class SetBalancesAndLimitsTransformer(
return WalletAdditionalInfo(hideable = true, infoContent)
}
private fun getRefreshTokenExpiredState(prevState: WalletState.Visa.Content): WalletState {
return WalletState.Visa.AccessTokenLocked(
walletCardState = prevState.walletCardState,
buttons = prevState.buttons,
bottomSheetConfig = prevState.bottomSheetConfig,
onExploreClick = clickIntents::onExploreClick,
onUnlockVisaAccessNotificationClick = clickIntents::onUnlockVisaAccessClick,
)
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber

View file

@ -10,7 +10,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletOnrampTransactionConverter
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.collections.immutable.toPersistentList
import timber.log.Timber

View file

@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState
import com.tangem.feature.wallet.presentation.wallet.state.model.DepositButtonState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.mutate
@ -35,6 +37,7 @@ internal class SetRefreshStateTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> prevState
}
}

View file

@ -61,8 +61,7 @@ internal class SetTokenListErrorTransformer(
title = title,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = selectedWallet),
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
balance = BigDecimal.ZERO.format {
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
},

View file

@ -9,7 +9,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensLis
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import timber.log.Timber
internal class SetTokenListTransformer(

View file

@ -7,7 +7,7 @@ import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
@ -33,6 +33,7 @@ internal class SetTxHistoryCountErrorTransformer(
is WalletState.Visa.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.w("Impossible to load transactions history for locked wallet")
prevState

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import timber.log.Timber
@ -26,6 +26,7 @@ internal class SetTxHistoryCountTransformer(
)
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.w("Impossible to load transactions history for locked wallet")
prevState

View file

@ -2,9 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.visa.exception.RefreshTokenExpiredException
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import timber.log.Timber
internal class SetTxHistoryItemsErrorTransformer(
@ -16,9 +17,10 @@ internal class SetTxHistoryItemsErrorTransformer(
override fun transform(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.SingleCurrency.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.Visa.Content -> prevState.copy(txHistoryState = createErrorState())
is WalletState.Visa.Content -> transformVisaContent(prevState)
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.w("Impossible to load transactions history for locked wallet")
prevState
@ -30,6 +32,20 @@ internal class SetTxHistoryItemsErrorTransformer(
}
}
private fun transformVisaContent(prevState: WalletState.Visa.Content): WalletState {
return if (error.cause is RefreshTokenExpiredException) {
WalletState.Visa.AccessTokenLocked(
walletCardState = prevState.walletCardState,
buttons = prevState.buttons,
bottomSheetConfig = prevState.bottomSheetConfig,
onExploreClick = clickIntents::onExploreClick,
onUnlockVisaAccessNotificationClick = clickIntents::onUnlockVisaAccessClick,
)
} else {
prevState.copy(txHistoryState = createErrorState())
}
}
private fun createErrorState(): TxHistoryState.Error = when (error) {
is TxHistoryListError.DataError -> {
TxHistoryState.Error(

View file

@ -6,7 +6,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemFlowConverter
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import kotlinx.coroutines.flow.Flow
import timber.log.Timber
@ -26,6 +26,7 @@ internal class SetTxHistoryItemsTransformer(
)
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.w("Impossible to load transactions history for locked wallet")
prevState

View file

@ -0,0 +1,80 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCardClickIntents
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
internal class SetWalletCardDropDownItemsTransformer(
private val dropdownEnabled: Boolean,
private val clickIntents: WalletCardClickIntents,
) : WalletScreenStateTransformer {
override fun transform(prevState: WalletScreenState): WalletScreenState {
return prevState.copy(wallets = prevState.wallets.map(::transformWalletState).toImmutableList())
}
private fun transformWalletState(prevState: WalletState): WalletState {
return when (prevState) {
is WalletState.MultiCurrency.Content -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.SingleCurrency.Content -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.Visa.Content -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.MultiCurrency.Locked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.SingleCurrency.Locked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.Visa.Locked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
is WalletState.Visa.AccessTokenLocked -> prevState.copy(
walletCardState = prevState.walletCardState.copySealed(
dropDownItems = constructDropDownItems(prevState.walletCardState.id),
),
)
}
}
private fun constructDropDownItems(userWalletId: UserWalletId): ImmutableList<WalletDropDownItems> {
return if (dropdownEnabled) {
persistentListOf(
WalletDropDownItems(
text = resourceReference(id = R.string.common_rename),
icon = R.drawable.ic_edit_24,
onClick = { clickIntents.onRenameBeforeConfirmationClick(userWalletId) },
),
WalletDropDownItems(
text = resourceReference(id = R.string.common_delete),
icon = R.drawable.ic_trash_24,
onClick = { clickIntents.onDeleteBeforeConfirmationClick(userWalletId) },
),
)
} else {
persistentListOf()
}
}
}

View file

@ -19,6 +19,7 @@ internal class SetWarningsTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.w("Impossible to update notifications for locked wallet")
prevState

View file

@ -6,7 +6,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.toImmutableList
import timber.log.Timber
@ -50,8 +50,9 @@ internal class UnlockWalletTransformer(
is WalletState.MultiCurrency.Content,
is WalletState.SingleCurrency.Content,
is WalletState.Visa.Content,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.e("Impossible to unlock wallet with content state")
Timber.e("Impossible to unlock wallet with not locked state")
prevState
}
}

View file

@ -27,6 +27,7 @@ internal class UpdateWalletCardsCountTransformer(
is WalletState.MultiCurrency.Locked,
is WalletState.SingleCurrency.Locked,
is WalletState.Visa.Locked,
is WalletState.Visa.AccessTokenLocked,
-> {
Timber.e("Impossible to update wallet cards count for locked wallet")
prevState

View file

@ -27,7 +27,6 @@ internal class BalancesAndLimitsBottomSheetConverter(
availableBalance = value.balances.available.let(::formatAmount),
blockedBalance = value.balances.blocked.let(::formatAmount),
debit = value.balances.debt.let(::formatAmount),
pending = value.balances.pendingRefund.let(::formatAmount),
amlVerified = value.balances.verified.let(::formatAmount),
onInfoClick = this::showBalanceInfo,
),

View file

@ -32,8 +32,7 @@ internal class MultiWalletCardStateConverter(
title = title,
additionalInfo = additionalInfo,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
)
}
@ -43,8 +42,7 @@ internal class MultiWalletCardStateConverter(
title = title,
additionalInfo = additionalInfo,
imageResId = imageResId,
onDeleteClick = onDeleteClick,
onRenameClick = onRenameClick,
dropDownItems = dropDownItems,
)
}
@ -54,8 +52,7 @@ internal class MultiWalletCardStateConverter(
title = title,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = selectedWallet),
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
balance = fiatBalance.amount.format {
fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol)
},

View file

@ -9,7 +9,7 @@ import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletCurrencyActionsClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletCurrencyActionsClickIntents
import com.tangem.utils.converter.Converter
import com.tangem.utils.isNullOrZero
import kotlinx.collections.immutable.ImmutableList

View file

@ -39,8 +39,7 @@ internal class SingleWalletCardStateConverter(
id = id,
title = title,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
)
}
@ -49,8 +48,7 @@ internal class SingleWalletCardStateConverter(
id = id,
title = title,
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
)
}
@ -63,8 +61,7 @@ internal class SingleWalletCardStateConverter(
currencyAmount = status.amount,
),
imageResId = imageResId,
onRenameClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
balance = formatFiatAmount(status = status, appCurrency = appCurrency),
cardCount = selectedWallet.getCardsCount(),
isZeroBalance = status.fiatAmount?.isZero(),

View file

@ -26,7 +26,7 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.persistentListOf

View file

@ -13,7 +13,7 @@ import com.tangem.domain.tokens.model.TotalFiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.mutate

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View file

@ -12,7 +12,7 @@ import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryItem.*
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.utils.StringsSigns.MINUS
import com.tangem.utils.StringsSigns.PLUS
import com.tangem.utils.converter.Converter

View file

@ -8,7 +8,7 @@ import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.visa.model.VisaTxDetails
import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents
import com.tangem.feature.wallet.child.wallet.model.intents.VisaWalletIntents
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
import org.joda.time.DateTimeZone

View file

@ -10,7 +10,7 @@ import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents
import com.tangem.feature.wallet.child.wallet.model.intents.VisaWalletIntents
import com.tangem.utils.StringsSigns
import com.tangem.utils.converter.Converter
import org.joda.time.DateTimeZone

View file

@ -8,7 +8,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
@ -90,8 +90,7 @@ internal class WalletLoadingStateFactory(
title = name,
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
imageResId = walletImageResolver.resolve(userWallet = this),
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
dropDownItems = persistentListOf(),
)
}

View file

@ -11,12 +11,12 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveIn
import kotlinx.coroutines.CoroutineScope

View file

@ -11,11 +11,11 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.model.TotalFiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
@Suppress("LongParameterList")

View file

@ -1,13 +1,13 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsSingleEventSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow

View file

@ -4,10 +4,10 @@ import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*

View file

@ -12,9 +12,9 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import timber.log.Timber

View file

@ -1,12 +1,12 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow

View file

@ -7,11 +7,11 @@ import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
@Suppress("LongParameterList")

View file

@ -13,6 +13,7 @@ import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountErrorTransformer
@ -20,7 +21,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHis
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

View file

@ -11,13 +11,13 @@ import com.tangem.domain.visa.GetVisaTxHistoryUseCase
import com.tangem.domain.visa.model.VisaCurrency
import com.tangem.domain.visa.model.VisaTxHistoryItem
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetBalancesAndLimitsTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxHistoryItemStateConverter
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch

View file

@ -0,0 +1,30 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWalletCardDropDownItemsTransformer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
internal class WalletDropDownItemsSubscriber(
private val stateHolder: WalletStateController,
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
private val clickIntents: WalletClickIntents,
) : WalletSubscriber() {
override fun create(coroutineScope: CoroutineScope): Flow<*> {
return flow<Any> {
shouldSaveUserWalletsUseCase.invoke()
.distinctUntilChanged()
.onEach {
stateHolder.update(
SetWalletCardDropDownItemsTransformer(
dropdownEnabled = it,
clickIntents = clickIntents,
),
)
}
.launchIn(coroutineScope)
}
}
}

View file

@ -67,6 +67,10 @@ import com.tangem.core.ui.test.TestTags
import com.tangem.core.ui.utils.lineTo
import com.tangem.core.ui.utils.moveTo
import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
@ -92,8 +96,6 @@ import kotlin.math.roundToInt
@Composable
internal fun WalletScreen(state: WalletScreenState, marketsEntryComponent: MarketsEntryComponent) {
BackHandler(onBack = state.onBackClick)
// It means that screen is still initializing
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return

View file

@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.*
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
@ -14,9 +13,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@ -30,6 +26,7 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -48,13 +45,14 @@ import com.tangem.core.ui.components.text.applyBladeBrush
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemDimens
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDropDownItems
import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach
import kotlinx.collections.immutable.ImmutableList
private const val HALF_OF_ITEM_WIDTH = 0.5
@ -71,8 +69,7 @@ private const val HALF_OF_ITEM_WIDTH = 0.5
internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
CardContainer(
onDeleteClick = { state.onDeleteClick(state.id) },
onRenameClick = { state.onRenameClick(state.id) },
dropDownItems = state.dropDownItems,
isLockedState = state is WalletCardState.LockedContent,
modifier = modifier,
) { itemSize ->
@ -148,8 +145,7 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi
@Composable
private fun CardContainer(
onDeleteClick: () -> Unit,
onRenameClick: () -> Unit,
dropDownItems: ImmutableList<WalletDropDownItems>,
isLockedState: Boolean,
modifier: Modifier = Modifier,
content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit),
@ -167,7 +163,7 @@ private fun CardContainer(
.defaultMinSize(minHeight = TangemTheme.dimens.size108)
.onSizeChanged { itemSize = it }
.then(
if (isLockedState) {
if (isLockedState || dropDownItems.isEmpty()) {
Modifier
} else {
Modifier
@ -210,8 +206,7 @@ private fun CardContainer(
pressOffset = pressOffset,
itemHeight = itemHeight,
onDismissRequest = { isMenuVisible = false },
onShowRenameWalletDialogClick = onRenameClick,
onDeleteClick = onDeleteClick,
dropDownItems = dropDownItems,
)
}
@ -222,8 +217,7 @@ private fun ManageWalletContextMenu(
pressOffset: DpOffset,
itemHeight: Dp,
onDismissRequest: () -> Unit,
onShowRenameWalletDialogClick: () -> Unit,
onDeleteClick: () -> Unit,
dropDownItems: ImmutableList<WalletDropDownItems>,
) {
DropdownMenu(
expanded = isMenuVisible,
@ -231,29 +225,23 @@ private fun ManageWalletContextMenu(
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
offset = pressOffset.copy(y = pressOffset.y - itemHeight),
) {
MenuItem(
textResId = R.string.common_rename,
imageVector = Icons.Outlined.Edit,
onClick = {
onDismissRequest()
onShowRenameWalletDialogClick()
},
)
MenuItem(
textResId = R.string.common_delete,
imageVector = Icons.Outlined.Delete,
onClick = {
onDismissRequest()
onDeleteClick()
},
)
dropDownItems.fastForEach { item ->
MenuItem(
text = item.text,
imageVector = ImageVector.vectorResource(id = item.icon),
onClick = {
onDismissRequest()
item.onClick()
},
)
}
}
}
@Composable
private fun MenuItem(@StringRes textResId: Int, imageVector: ImageVector, onClick: () -> Unit) {
private fun MenuItem(text: TextReference, imageVector: ImageVector, onClick: () -> Unit) {
DropdownMenuItem(
text = { Text(text = stringResourceSafe(id = textResId), style = TangemTheme.typography.subtitle2) },
text = { Text(text = text.resolveReference(), style = TangemTheme.typography.subtitle2) },
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
trailingIcon = { Icon(imageVector = imageVector, contentDescription = null) },
onClick = onClick,

View file

@ -78,10 +78,6 @@ private fun BalancesBlock(balances: BalancesAndLimitsBottomSheetConfig.Balance,
title = stringReference("Debit"),
value = balances.debit,
)
BlockItem(
title = stringReference("Pending refund"),
value = balances.pending,
)
},
description = {
InfoButton(onClick = balances.onInfoClick)
@ -180,7 +176,6 @@ private class BalancesAndLimitsBottomSheetParameterProvider :
availableBalance = "392.45 USDT",
blockedBalance = "36.00 USDT",
debit = "00.00 USDT",
pending = "20.99 USDT",
amlVerified = "356.45 USDT",
onInfoClick = {},
),

View file

@ -1,23 +1,22 @@
package com.tangem.feature.wallet.presentation.wallet.utils
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import dagger.hilt.android.scopes.ViewModelScoped
import com.arkivanov.essenty.lifecycle.Lifecycle
import com.tangem.core.decompose.di.ModelScoped
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@ViewModelScoped
internal class ScreenLifecycleProvider @Inject constructor() : DefaultLifecycleObserver {
@ModelScoped
internal class ScreenLifecycleProvider @Inject constructor() : Lifecycle.Callbacks {
private val _isBackgroundState = MutableStateFlow(false)
val isBackgroundState: StateFlow<Boolean> = _isBackgroundState
override fun onResume(owner: LifecycleOwner) {
override fun onResume() {
_isBackgroundState.value = false
}
override fun onPause(owner: LifecycleOwner) {
override fun onPause() {
_isBackgroundState.value = true
}
}