From a38bc0322d4ceaeef24a3e62500ccdb4904bda49 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Oct 2023 16:36:42 +0300 Subject: [PATCH 01/12] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 42 ++++--- .../walletconnect/WalletConnectMiddleware.kt | 111 +++++++++++------- .../DefaultWalletManagersStore.kt | 4 + .../walletmanager/WalletManagersStore.kt | 2 + .../domain/common/extensions/Blockchain.kt | 2 - .../DefaultWalletManagersFacade.kt | 4 + .../walletmanager/WalletManagersFacade.kt | 2 + 7 files changed, 107 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index c10b88280a..67f1253c9e 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -92,11 +92,7 @@ class TapWalletManager( store.dispatchWalletAction(action = WalletAction.Warnings.CheckIfNeeded) } setupWalletConnectV2(userWallet) - - val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - if (!walletFeatureToggles.isRedesignedScreenEnabled) { - loadData(userWallet = userWallet, refresh = refresh) - } + loadData(userWallet = userWallet, refresh = refresh) } private fun Store.dispatchWalletAction(action: WalletAction) { @@ -161,21 +157,31 @@ class TapWalletManager( } } - private fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List { - return store.state.walletState.walletManagers - .mapNotNull { - val wallet = it.wallet - val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull( - wallet.blockchain.toNetworkId(), + private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List { + val walletManagerToggles = store.state.daggerGraphState + .get(DaggerGraphState::walletFeatureToggles) + val walletManagers = if (walletManagerToggles.isRedesignedScreenEnabled) { + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList() + walletManagerFacade.getStoredWalletManagers(userWallet.walletId) + } else { + store.state.walletState.walletManagers + } + + return walletManagers.mapNotNull { + val wallet = it.wallet + val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull( + wallet.blockchain.toNetworkId(), + ) + chainId?.let { + Account( + chainId, + wallet.address, + wallet.publicKey.derivationPath?.rawPath, ) - chainId?.let { - Account( - chainId, - wallet.address, - wallet.publicKey.derivationPath?.rawPath, - ) - } } + } } fun updateConfigManager(data: ScanResponse) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index a436d55d8e..a9f177a893 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -32,6 +32,7 @@ import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.tap.userWalletsListManager import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -246,47 +247,50 @@ class WalletConnectMiddleware { store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedNetwork())) return } - val walletManager = getWalletManager( - wallet = action.session.wallet, - blockchain = blockchain, - walletState = store.state.walletState, - ).guard { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.AddNetwork(blockchain.fullName), - ), + scope.launch { + val walletManager = getWalletManager( + wallet = action.session.wallet, + blockchain = blockchain, + walletState = store.state.walletState, + ).guard { + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.AddNetwork(blockchain.fullName), + ), + ) + return@launch + } + val updatedWallet = action.session.wallet.copy( + walletPublicKey = walletManager.wallet.publicKey.seedKey, + derivedPublicKey = walletManager.wallet.publicKey.derivedKey, + derivationPath = walletManager.wallet.publicKey.derivationPath, + blockchain = action.blockchain, ) - return + val updatedSession = action.session.copy(wallet = updatedWallet) + store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession)) } - val updatedWallet = action.session.wallet.copy( - walletPublicKey = walletManager.wallet.publicKey.seedKey, - derivedPublicKey = walletManager.wallet.publicKey.derivedKey, - derivationPath = walletManager.wallet.publicKey.derivationPath, - blockchain = action.blockchain, - ) - val updatedSession = action.session.copy(wallet = updatedWallet) - store.dispatchOnMain(WalletConnectAction.UpdateBlockchain(updatedSession)) } is WalletConnectAction.UpdateBlockchain -> { walletConnectManager.updateBlockchain(action.updatedSession) } is WalletConnectAction.ApproveProposal -> { - val accounts = store.state.walletState.walletManagers - .mapNotNull { - val wallet = it.wallet - val chainId = walletConnectInteractor.blockchainHelper.networkIdToChainIdOrNull( - wallet.blockchain.toNetworkId(), - ) - chainId?.let { - Account( - chainId, - wallet.address, - wallet.publicKey.derivationPath?.rawPath, + scope.launch { + val accounts = getWalletManagers() + .mapNotNull { + val wallet = it.wallet + val chainId = walletConnectInteractor.blockchainHelper.networkIdToChainIdOrNull( + wallet.blockchain.toNetworkId(), ) + chainId?.let { + Account( + chainId, + wallet.address, + wallet.publicKey.derivationPath?.rawPath, + ) + } } - } - - walletConnectInteractor.approveSessionProposal(accounts) + walletConnectInteractor.approveSessionProposal(accounts) + } } is WalletConnectAction.RejectProposal -> { walletConnectInteractor.rejectSessionProposal() @@ -345,6 +349,19 @@ class WalletConnectMiddleware { } } + private suspend fun getWalletManagers(): List { + val walletManagerToggles = store.state.daggerGraphState + .get(DaggerGraphState::walletFeatureToggles) + return if (walletManagerToggles.isRedesignedScreenEnabled) { + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList() + walletManagerFacade.getStoredWalletManagers(userWallet.walletId) + } else { + store.state.walletState.walletManagers + } + } + private fun scanCard(scanResponse: ScanResponse, session: WalletConnectSession, chainId: Int?) { val blockchain = WalletConnectNetworkUtils.parseBlockchain( chainId = chainId, @@ -430,7 +447,7 @@ class WalletConnectMiddleware { ) } - private fun getWalletManager( + private suspend fun getWalletManager( wallet: WalletForSession, blockchain: Blockchain, walletState: WalletState, @@ -440,15 +457,29 @@ class WalletConnectMiddleware { } else { blockchain } + val userWallet = userWalletsListManager.selectedUserWalletSync ?: return null val derivation = blockchainToMake.derivationPath( - style = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle(), + style = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(), )?.rawPath - val blockchainNetwork = BlockchainNetwork( - blockchain = blockchainToMake, - derivationPath = derivation, - tokens = emptyList(), - ) - return walletState.getWalletManager(blockchainNetwork) + val walletFeatureToggles = store.state.daggerGraphState + .get(DaggerGraphState::walletFeatureToggles) + + return if (walletFeatureToggles.isRedesignedScreenEnabled) { + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + walletManagerFacade.getOrCreateWalletManager( + userWalletId = userWallet.walletId, + blockchain = blockchainToMake, + derivationPath = derivation, + ) + } else { + val blockchainNetwork = BlockchainNetwork( + blockchain = blockchainToMake, + derivationPath = derivation, + tokens = emptyList(), + ) + walletState.getWalletManager(blockchainNetwork) + } } private fun isWalletConnectUri(uri: String): Boolean { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt index 1222edafb4..f99db26624 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt @@ -33,6 +33,10 @@ internal class DefaultWalletManagersStore( } } + override suspend fun getAllSync(userWalletId: UserWalletId): List { + return getSyncOrNull(userWalletId) ?: emptyList() + } + override suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) { val walletManagers = getSyncOrNull(userWalletId) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt index 8506eb5803..a820b1d7c9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/WalletManagersStore.kt @@ -15,6 +15,8 @@ interface WalletManagersStore { derivationPath: String?, ): WalletManager? + suspend fun getAllSync(userWalletId: UserWalletId): List + suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) suspend fun clear() diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 0970e85d77..a37ce82bcb 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -212,6 +212,4 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Ducatus, - Blockchain.Telos, // disable in 4.9 - Blockchain.TelosTestnet, // disable in 4.9 ) \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 9851ae5bc8..01a025f741 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -221,6 +221,10 @@ class DefaultWalletManagersFacade( return walletManager } + override suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List { + return walletManagersStore.getAllSync(userWalletId) + } + override suspend fun getAddress(userWalletId: UserWalletId, network: Network): List
{ val blockchain = Blockchain.fromId(network.id.value) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 94ce68d1d3..2a30480141 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -77,6 +77,8 @@ interface WalletManagersFacade { derivationPath: String?, ): WalletManager? + suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List + /** * Returns ordered list of addresses for selected wallet for given currency * From bf31324d3eedb33603a2d46c17a9ef4fb23d18ae Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 5 Oct 2023 16:35:37 +0300 Subject: [PATCH 02/12] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 8 ++- .../walletconnect/WalletConnectMiddleware.kt | 42 ++++++++++++ .../walletconnect/WalletConnectActions.kt | 12 ++++ .../walletmanager/WalletManagersFacade.kt | 1 + .../wallet/viewmodels/WalletViewModel.kt | 67 ++++++++++++++++++- 5 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 domain/legacy/src/main/java/com/tangem/domain/walletconnect/WalletConnectActions.kt diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 67f1253c9e..729e96fe27 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -91,8 +91,12 @@ class TapWalletManager( store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed)) store.dispatchWalletAction(action = WalletAction.Warnings.CheckIfNeeded) } - setupWalletConnectV2(userWallet) - loadData(userWallet = userWallet, refresh = refresh) + + val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) + if (!walletFeatureToggles.isRedesignedScreenEnabled) { + setupWalletConnectV2(userWallet) + loadData(userWallet = userWallet, refresh = refresh) + } } private fun Store.dispatchWalletAction(action: WalletAction) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index a9f177a893..4ed140a246 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -14,6 +14,8 @@ import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.walletconnect.WalletConnectActions +import com.tangem.domain.wallets.models.UserWallet import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -60,6 +62,28 @@ class WalletConnectMiddleware { if (DemoHelper.tryHandle(state, action)) return when (action) { + is WalletConnectActions.New.Initialize -> { + val userWallet = action.userWallet + val cardId = if (userWallet.scanResponse.card.backupStatus?.isActive != true) { + userWallet.cardId + } else { // if wallet has backup, any card from wallet can be used to sign + null + } + scope.launch { + val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch + wcInteractor.startListening( + userWalletId = userWallet.walletId.stringValue, + cardId = cardId, + ) + } + } + is WalletConnectActions.New.SetupUserChains -> { + scope.launch { + val userWallet = action.userWallet + val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch + wcInteractor.setUserChains(getAccountsForWc(wcInteractor, userWallet)) + } + } is WalletConnectAction.ResetState -> walletConnectManager = WalletConnectManager() is WalletConnectAction.RestoreSessions -> { walletConnectManager.restoreSessions(action.scanResponse) @@ -485,4 +509,22 @@ class WalletConnectMiddleware { private fun isWalletConnectUri(uri: String): Boolean { return WalletConnectManager.isCorrectWcUri(uri) || walletConnectInteractor.isWalletConnectUri(uri) } + + private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor, userWallet: UserWallet): List { + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + return walletManagerFacade.getStoredWalletManagers(userWallet.walletId).mapNotNull { + val wallet = it.wallet + val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull( + wallet.blockchain.toNetworkId(), + ) + chainId?.let { + Account( + chainId, + wallet.address, + wallet.publicKey.derivationPath?.rawPath, + ) + } + } + } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletconnect/WalletConnectActions.kt b/domain/legacy/src/main/java/com/tangem/domain/walletconnect/WalletConnectActions.kt new file mode 100644 index 0000000000..afb7d26d9f --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/walletconnect/WalletConnectActions.kt @@ -0,0 +1,12 @@ +package com.tangem.domain.walletconnect + +import com.tangem.domain.wallets.models.UserWallet +import org.rekotlin.Action + +sealed class WalletConnectActions : Action { + sealed class New { + data class Initialize(val userWallet: UserWallet) : WalletConnectActions() + + data class SetupUserChains(val userWallet: UserWallet) : WalletConnectActions() + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 2a30480141..6df4bf3f84 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -77,6 +77,7 @@ interface WalletManagersFacade { derivationPath: String?, ): WalletManager? + @Deprecated("Will be removed in future") suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index e860f3d518..9cb22da162 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import androidx.lifecycle.* import androidx.paging.cachedIn +import arrow.core.Either import arrow.core.getOrElse import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.Blockchain @@ -39,6 +40,7 @@ import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.* import com.tangem.domain.tokens.* +import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -48,6 +50,7 @@ import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.userwallets.UserWalletBuilder +import com.tangem.domain.walletconnect.WalletConnectActions import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -158,6 +161,7 @@ internal class WalletViewModel @Inject constructor( private var singleWalletCryptoCurrencyStatus: CryptoCurrencyStatus? = null private val tokensJobHolder = JobHolder() + private val updateWcJobHolder = JobHolder() private val marketPriceJobHolder = JobHolder() private val buttonsJobHolder = JobHolder() private val notificationsJobHolder = JobHolder() @@ -455,6 +459,7 @@ internal class WalletViewModel @Inject constructor( * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. */ tokensJobHolder.update(job = null) + updateWcJobHolder.update(job = null) marketPriceJobHolder.update(job = null) buttonsJobHolder.update(job = null) notificationsJobHolder.update(job = null) @@ -851,6 +856,7 @@ internal class WalletViewModel @Inject constructor( * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. */ tokensJobHolder.update(job = null) + updateWcJobHolder.update(job = null) marketPriceJobHolder.update(job = null) buttonsJobHolder.update(job = null) notificationsJobHolder.update(job = null) @@ -862,17 +868,22 @@ internal class WalletViewModel @Inject constructor( wallet.isLocked -> { uiState = stateFactory.getLockedState() } - wallet.isMultiCurrency -> getMultiCurrencyContent(index) + wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index) !wallet.isMultiCurrency -> getSingleCurrencyContent(index) } } - private fun getMultiCurrencyContent(walletIndex: Int) { + private fun getMultiCurrencyContent(wallet: UserWallet, walletIndex: Int) { val state = requireNotNull(uiState as? WalletMultiCurrencyState) { "Impossible to get a token list updates if state isn't WalletMultiCurrencyState" } - getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) + val tokenListFlow = getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) + .shareIn(viewModelScope, SharingStarted.WhileSubscribed()) + + initAndSetupWc(tokenListFlow, wallet) + + tokenListFlow .distinctUntilChanged() .onEach { maybeTokenList -> uiState = stateFactory.getStateByTokensList(maybeTokenList) @@ -905,6 +916,44 @@ internal class WalletViewModel @Inject constructor( } } + private fun initAndSetupWc(tokenListFlow: SharedFlow>, wallet: UserWallet) { + initWalletConnectForWallet(wallet) + tokenListFlow + .filter(::filterLoadedTokenList) + .take(1) + .onEach { + it.onRight { + setupWalletConnectOnWallet(wallet) + } + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(updateWcJobHolder) + } + + private fun List.isAllCurrenciesLoaded(): Boolean { + return !this.any { it.value is CryptoCurrencyStatus.Loading } + } + + private fun filterLoadedTokenList(either: Either): Boolean { + return either.fold( + ifRight = { list -> + when (list) { + is TokenList.Ungrouped -> { + list.currencies.isAllCurrenciesLoaded() + } + is TokenList.GroupedByNetwork -> { + list.groups.flatMap { group -> group.currencies }.isAllCurrenciesLoaded() + } + else -> { + false + } + } + }, + ifLeft = { false }, + ) + } + private fun List.hasNonZeroWallets(): Boolean { return any { val amount = it.value.amount ?: return@any false @@ -1023,6 +1072,18 @@ internal class WalletViewModel @Inject constructor( ) } + private fun initWalletConnectForWallet(userWallet: UserWallet) { + reduxStateHolder.dispatch( + WalletConnectActions.New.Initialize(userWallet = userWallet), + ) + } + + private fun setupWalletConnectOnWallet(userWallet: UserWallet) { + reduxStateHolder.dispatch( + WalletConnectActions.New.SetupUserChains(userWallet = userWallet), + ) + } + private fun getWallet(index: Int): UserWallet { return requireNotNull( value = wallets.getOrNull(index), From 519f5332f1d1549cb25d6c1ba901f5e43f4819a8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Oct 2023 08:58:37 +0300 Subject: [PATCH 03/12] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 6 + .../middlewares/MultiWalletMiddleware.kt | 4 + core/datasource/build.gradle.kts | 1 + .../di/TxHistoryItemsStoreModule.kt | 21 +++ .../txhistory/DefaultTxHistoryItemsStore.kt | 42 ++++++ .../local/txhistory/TxHistoryItemsStore.kt | 22 +++ data/txhistory/build.gradle.kts | 5 +- .../data/txhistory/di/TxHistoryDataModule.kt | 10 +- .../repository/DefaultTxHistoryRepository.kt | 45 +++--- .../paging/TxHistoryPagingSource.kt | 88 ++++++++++-- domain/txhistory/build.gradle.kts | 14 +- .../repository/TxHistoryRepository.kt | 10 +- .../usecase/GetTxHistoryItemsCountUseCase.kt | 7 +- .../usecase/GetTxHistoryItemsUseCase.kt | 6 +- .../wallets/models/GetSelectedWalletError.kt | 8 -- .../wallets/models/GetUserWalletError.kt | 8 ++ .../usecase/GetSelectedWalletUseCase.kt | 12 +- .../wallets/usecase/GetUserWalletUseCase.kt | 30 ++++ .../navigation/TokenDetailsRouter.kt | 1 + .../viewmodels/TokenDetailsViewModel.kt | 130 +++++++++--------- .../router/DefaultWalletRouter.kt | 7 +- .../presentation/router/InnerWalletRouter.kt | 2 +- .../wallet/viewmodels/WalletViewModel.kt | 32 ++++- 23 files changed, 379 insertions(+), 132 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt delete mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetUserWalletError.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetUserWalletUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 8e3d763c56..2d12ae7b0b 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -20,6 +20,12 @@ internal object WalletsDomainModule { return GetWalletsUseCase(walletsStateHolder = walletsStateHolder) } + @Provides + @ViewModelScoped + fun providesGetUserWalletUseCase(walletsStateHolder: WalletsStateHolder): GetUserWalletUseCase { + return GetUserWalletUseCase(walletsStateHolder = walletsStateHolder) + } + @Provides @ViewModelScoped fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index eb198a07f9..74aaf39091 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -39,9 +39,13 @@ class MultiWalletMiddleware { when (action) { is WalletAction.MultiWallet.SelectWallet -> { if (action.currency != null) { + val userWalletId = userWalletsListManager.selectedUserWalletSync?.walletId + val bundle = bundleOf( + TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId?.stringValue, TokenDetailsRouter.CRYPTO_CURRENCY_KEY to cryptoCurrencyConverter.convert(action.currency), ) + store.dispatch(NavigationAction.NavigateTo(screen = AppScreen.WalletDetails, bundle = bundle)) } } diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 79b6816470..6d7e66c87e 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.balanceHiding.models) + implementation(projects.domain.txhistory.models) /** Tangem libraries */ implementation(deps.tangem.blockchain) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt new file mode 100644 index 0000000000..9a936f57f0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/TxHistoryItemsStoreModule.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.local.datastore.RuntimeDataStore +import com.tangem.datasource.local.txhistory.DefaultTxHistoryItemsStore +import com.tangem.datasource.local.txhistory.TxHistoryItemsStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal object TxHistoryItemsStoreModule { + + @Provides + fun provideTxHistoryItemsStore(): TxHistoryItemsStore { + return DefaultTxHistoryItemsStore( + dataStore = RuntimeDataStore(), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt new file mode 100644 index 0000000000..7cbeb3f317 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt @@ -0,0 +1,42 @@ +package com.tangem.datasource.local.txhistory + +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator +import com.tangem.domain.txhistory.models.PaginationWrapper +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.utils.extensions.addOrReplace + +internal class DefaultTxHistoryItemsStore( + dataStore: StringKeyDataStore>>, +) : TxHistoryItemsStore, + StringKeyDataStoreDecorator>>(dataStore) { + + override fun provideStringKey(key: TxHistoryItemsStore.Key): String = key.toString() + + override suspend fun getNextPageSyncOrNull(key: TxHistoryItemsStore.Key): Int? { + val storedValue = getSyncOrNull(key) ?: return null + val lastWrappedItems = storedValue.maxBy(PaginationWrapper<*>::page) + val lastPage = lastWrappedItems.page + + return if (lastPage <= lastWrappedItems.totalPages) { + lastPage + } else { + null + } + } + + override suspend fun getSyncOrNull(key: TxHistoryItemsStore.Key, page: Int): PaginationWrapper? { + val storedValue = getSyncOrNull(key) + + return storedValue?.firstOrNull { it.page == page } + } + + override suspend fun store(key: TxHistoryItemsStore.Key, value: PaginationWrapper) { + val oldValue = getSyncOrNull(key).orEmpty() + val newValue = oldValue.addOrReplace(value) { + it.page == value.page + } + + store(key, newValue) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt new file mode 100644 index 0000000000..e2f288e235 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt @@ -0,0 +1,22 @@ +package com.tangem.datasource.local.txhistory + +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.txhistory.models.PaginationWrapper +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.wallets.models.UserWalletId + +interface TxHistoryItemsStore { + + suspend fun getNextPageSyncOrNull(key: Key): Int? + + suspend fun getSyncOrNull(key: Key, page: Int): PaginationWrapper? + + suspend fun remove(key: Key) + + suspend fun store(key: Key, value: PaginationWrapper) + + data class Key( + val userWalletId: UserWalletId, + val currency: CryptoCurrency, + ) +} \ No newline at end of file diff --git a/data/txhistory/build.gradle.kts b/data/txhistory/build.gradle.kts index 7ffda888cc..d53b060232 100644 --- a/data/txhistory/build.gradle.kts +++ b/data/txhistory/build.gradle.kts @@ -10,6 +10,8 @@ android { } dependencies { + implementation(projects.data.common) + implementation(projects.core.utils) implementation(projects.core.datasource) implementation(projects.domain.legacy) @@ -20,7 +22,8 @@ dependencies { implementation(deps.kotlin.coroutines) implementation(deps.androidx.paging.runtime) - implementation(deps.arrow.core) + implementation(deps.timber) + implementation(deps.jodatime) implementation(deps.hilt.core) kapt(deps.hilt.kapt) diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt index da8b08603c..27f3b8e7bb 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt @@ -1,6 +1,8 @@ package com.tangem.data.txhistory.di +import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.txhistory.repository.DefaultTxHistoryRepository +import com.tangem.datasource.local.txhistory.TxHistoryItemsStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.txhistory.repository.TxHistoryRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -17,10 +19,14 @@ internal object TxHistoryDataModule { @Provides @Singleton fun provideTxHistoryRepository( + cacheRegistry: CacheRegistry, walletManagersFacade: WalletManagersFacade, userWalletsStore: UserWalletsStore, + txHistoryItemsStore: TxHistoryItemsStore, ): TxHistoryRepository = DefaultTxHistoryRepository( - walletManagersFacade = walletManagersFacade, - userWalletsStore = userWalletsStore, + cacheRegistry, + walletManagersFacade, + userWalletsStore, + txHistoryItemsStore, ) } \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt index 2f9fa852c0..bfa1cc57d1 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt @@ -3,7 +3,9 @@ package com.tangem.data.txhistory.repository import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData +import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource +import com.tangem.datasource.local.txhistory.TxHistoryItemsStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network @@ -13,50 +15,57 @@ import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.repository.TxHistoryRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow class DefaultTxHistoryRepository( + private val cacheRegistry: CacheRegistry, private val walletManagersFacade: WalletManagersFacade, private val userWalletsStore: UserWalletsStore, + private val txHistoryItemsStore: TxHistoryItemsStore, ) : TxHistoryRepository { - override suspend fun getTxHistoryItemsCount(network: Network): Int { - val userWallet = getUserWallet() + override suspend fun getTxHistoryItemsCount(userWalletId: UserWalletId, network: Network): Int { + val userWallet = getUserWallet(userWalletId) val state = walletManagersFacade.getTxHistoryState( userWalletId = userWallet.walletId, network = network, ) return when (state) { is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception) - TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented - TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories + is TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented + is TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories is TxHistoryState.Success.HasTransactions -> state.txCount } } - override fun getTxHistoryItems(currency: CryptoCurrency, pageSize: Int): Flow> { - val userWallet = getUserWallet() - return Pager( + override fun getTxHistoryItems( + userWalletId: UserWalletId, + currency: CryptoCurrency, + pageSize: Int, + refresh: Boolean, + ): Flow> { + val pager = Pager( config = PagingConfig( pageSize = pageSize, initialLoadSize = pageSize, ), pagingSourceFactory = { TxHistoryPagingSource( - loadPage = { page: Int, pageSize: Int -> - walletManagersFacade.getTxHistoryItems( - userWalletId = userWallet.walletId, - currency = currency, - page = page, - pageSize = pageSize, - ) - }, + sourceParams = TxHistoryPagingSource.Params(userWalletId, currency, pageSize, refresh), + txHistoryItemsStore = txHistoryItemsStore, + walletManagersFacade = walletManagersFacade, + cacheRegistry = cacheRegistry, ) }, - ).flow + ) + + return pager.flow } - private fun getUserWallet(): UserWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull) { - "Selected wallet must not be null" + private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet { + return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find user wallet with provided ID: $userWalletId" + } } } \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt index a3ebb06194..f195b173f7 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt @@ -2,34 +2,96 @@ package com.tangem.data.txhistory.repository.paging import androidx.paging.PagingSource import androidx.paging.PagingState +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.datasource.local.txhistory.TxHistoryItemsStore +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem - -private const val INITIAL_PAGE = 1 +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId +import timber.log.Timber internal class TxHistoryPagingSource( - private val loadPage: suspend (page: Int, pageSize: Int) -> PaginationWrapper, + private val sourceParams: Params, + private val txHistoryItemsStore: TxHistoryItemsStore, + private val walletManagersFacade: WalletManagersFacade, + private val cacheRegistry: CacheRegistry, ) : PagingSource() { + private val storeKey = TxHistoryItemsStore.Key(sourceParams.userWalletId, sourceParams.currency) + override fun getRefreshKey(state: PagingState): Int? { return state.anchorPosition?.let { anchorPosition -> - state.closestPageToPosition(anchorPosition)?.prevKey?.plus(other = 1) - ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(other = 1) + val anchorPage = state.closestPageToPosition(anchorPosition) + anchorPage?.prevKey?.inc() ?: anchorPage?.nextKey?.dec() } } override suspend fun load(params: LoadParams): LoadResult { - val currentPage = params.key ?: INITIAL_PAGE - return try { - val result = loadPage(currentPage, params.loadSize) + val pageToLoad = params.key ?: INITIAL_PAGE - LoadResult.Page( - data = result.items, - prevKey = if (currentPage > INITIAL_PAGE) currentPage.minus(1) else null, - nextKey = if (result.page < result.totalPages) currentPage.plus(1) else null, + return try { + val wrappedItems = loadItems( + pageToLoad = pageToLoad, + pageSize = sourceParams.pageSize, + refresh = sourceParams.refresh && params is LoadParams.Refresh, ) - } catch (e: Exception) { + + val items = wrappedItems.items + val prevPage = when { + items.isEmpty() -> null + pageToLoad > INITIAL_PAGE -> pageToLoad.dec() + else -> null + } + val nextPage = when { + items.isEmpty() -> INITIAL_PAGE + pageToLoad < wrappedItems.totalPages -> pageToLoad.inc() + else -> null + } + + LoadResult.Page(items, prevKey = prevPage, nextKey = nextPage) + } catch (e: Throwable) { + Timber.e(e, "Unable to load the transaction history for the requested page: $pageToLoad") + LoadResult.Error(e) } } + + private suspend fun loadItems(pageToLoad: Int, pageSize: Int, refresh: Boolean): PaginationWrapper { + cacheRegistry.invokeOnExpire( + key = getTxHistoryPageKey(pageToLoad), + skipCache = refresh, + block = { fetch(pageToLoad, pageSize) }, + ) + + return requireNotNull(txHistoryItemsStore.getSyncOrNull(storeKey, pageToLoad)) { + "The transaction history page #$pageToLoad could not be retrieved" + } + } + + private suspend fun fetch(pageToLoad: Int, pageSize: Int) { + val wrappedItems = walletManagersFacade.getTxHistoryItems( + userWalletId = sourceParams.userWalletId, + currency = sourceParams.currency, + page = pageToLoad, + pageSize = pageSize, + ) + + txHistoryItemsStore.store(storeKey, wrappedItems) + } + + private fun getTxHistoryPageKey(page: Int): String { + return "tx_history_page_${sourceParams.currency}_${sourceParams.userWalletId}_$page" + } + + data class Params( + val userWalletId: UserWalletId, + val currency: CryptoCurrency, + val pageSize: Int, + val refresh: Boolean, + ) + + private companion object { + private const val INITIAL_PAGE = 1 + } } \ No newline at end of file diff --git a/domain/txhistory/build.gradle.kts b/domain/txhistory/build.gradle.kts index 2ba0547f59..892e55e639 100644 --- a/domain/txhistory/build.gradle.kts +++ b/domain/txhistory/build.gradle.kts @@ -9,11 +9,15 @@ android { } dependencies { - implementation(deps.arrow.core) - implementation(deps.kotlin.coroutines) - implementation(deps.androidx.paging.runtime) - - implementation(projects.core.utils) + /** Project - Domain */ + implementation(projects.domain.core) implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory.models) + implementation(projects.domain.wallets.models) + + /** Project - Other */ + implementation(projects.core.utils) + + /** Android - Other */ + implementation(deps.androidx.paging.runtime) } \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt index 0c778f81ae..a71fe087c1 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt @@ -6,13 +6,19 @@ import com.tangem.domain.tokens.model.Network import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow interface TxHistoryRepository { @Throws(TxHistoryStateError::class) - suspend fun getTxHistoryItemsCount(network: Network): Int + suspend fun getTxHistoryItemsCount(userWalletId: UserWalletId, network: Network): Int @Throws(TxHistoryListError::class) - fun getTxHistoryItems(currency: CryptoCurrency, pageSize: Int): Flow> + fun getTxHistoryItems( + userWalletId: UserWalletId, + currency: CryptoCurrency, + pageSize: Int, + refresh: Boolean, + ): Flow> } \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt index 6ca59cb013..ab14187a02 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt @@ -6,14 +6,15 @@ import arrow.core.raise.either import com.tangem.domain.tokens.model.Network import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.repository.TxHistoryRepository +import com.tangem.domain.wallets.models.UserWalletId +// TODO: Add tests class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) { - // FIXME: Provide UserWalletId - suspend operator fun invoke(network: Network): Either { + suspend operator fun invoke(userWalletId: UserWalletId, network: Network): Either { return either { catch( - block = { repository.getTxHistoryItemsCount(network) }, + block = { repository.getTxHistoryItemsCount(userWalletId, network) }, catch = { throwable -> raise( when (throwable) { diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt index 0555f17187..e5570e8077 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt @@ -7,21 +7,25 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.repository.TxHistoryRepository +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch private const val DEFAULT_PAGE_SIZE = 50 +// TODO: Add tests class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) { // FIXME: Provide UserWalletId operator fun invoke( + userWalletId: UserWalletId, currency: CryptoCurrency, pageSize: Int = DEFAULT_PAGE_SIZE, + refresh: Boolean = false, ): Either>> { return either { repository - .getTxHistoryItems(currency = currency, pageSize = pageSize) + .getTxHistoryItems(userWalletId, currency, pageSize, refresh) .catch { raise(TxHistoryListError.DataError(it)) } } } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt deleted file mode 100644 index ad0867fa6e..0000000000 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.domain.wallets.models - -sealed interface GetSelectedWalletError { - - object DataError : GetSelectedWalletError - - object NoUserWalletSelected : GetSelectedWalletError -} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetUserWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetUserWalletError.kt new file mode 100644 index 0000000000..50d1bea262 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetUserWalletError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.wallets.models + +sealed class GetUserWalletError { + + data class DataError(val cause: Throwable) : GetUserWalletError() + + object UserWalletNotFound : GetUserWalletError() +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt index b56de58375..a29aad4ea4 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt @@ -4,7 +4,7 @@ import arrow.core.Either import arrow.core.raise.either import arrow.core.raise.ensureNotNull import com.tangem.domain.wallets.legacy.WalletsStateHolder -import com.tangem.domain.wallets.models.GetSelectedWalletError +import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.UserWallet /** @@ -17,16 +17,20 @@ import com.tangem.domain.wallets.models.UserWallet */ class GetSelectedWalletUseCase(private val walletsStateHolder: WalletsStateHolder) { - operator fun invoke(): Either { + operator fun invoke(): Either { return either { val userWalletsListManager = ensureNotNull( value = walletsStateHolder.userWalletsListManager, - raise = { GetSelectedWalletError.DataError }, + raise = { + val error = IllegalStateException("User wallets list manager not initialized") + + GetUserWalletError.DataError(error) + }, ) ensureNotNull( value = userWalletsListManager.selectedUserWalletSync, - raise = { GetSelectedWalletError.NoUserWalletSelected }, + raise = { GetUserWalletError.UserWalletNotFound }, ) } } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetUserWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetUserWalletUseCase.kt new file mode 100644 index 0000000000..9d1fe85f5e --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetUserWalletUseCase.kt @@ -0,0 +1,30 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensureNotNull +import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.models.GetUserWalletError +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.firstOrNull + +class GetUserWalletUseCase(private val walletsStateHolder: WalletsStateHolder) { + + suspend operator fun invoke(userWalletId: UserWalletId): Either = either { + val userWalletsListManager = ensureNotNull( + value = walletsStateHolder.userWalletsListManager, + raise = { + val error = IllegalStateException("User wallets list manager not initialized") + + GetUserWalletError.DataError(error) + }, + ) + + val userWallets = userWalletsListManager.userWallets.firstOrNull().orEmpty() + + ensureNotNull(userWallets.firstOrNull { it.walletId == userWalletId }) { + raise(GetUserWalletError.UserWalletNotFound) + } + } +} \ No newline at end of file diff --git a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt index da5bc7571f..a0f630c125 100644 --- a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt +++ b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt @@ -7,6 +7,7 @@ interface TokenDetailsRouter { fun getEntryFragment(): Fragment companion object { + const val USER_WALLET_ID_KEY = "token_details_user_wallet_id" const val CRYPTO_CURRENCY_KEY = "token_details_crypto_currency" } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index bbf6300015..1de3829916 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -23,10 +23,9 @@ import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase -import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenScreenEvent import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState @@ -48,7 +47,7 @@ import kotlin.properties.Delegates @HiltViewModel internal class TokenDetailsViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, - private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val getUserWalletUseCase: GetUserWalletUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, @@ -67,15 +66,18 @@ internal class TokenDetailsViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents { + private val userWalletId: UserWalletId = savedStateHandle.get(TokenDetailsRouter.USER_WALLET_ID_KEY) + ?.let { stringValue -> UserWalletId(stringValue) } + ?: error("This screen can't open without `UserWalletId`") + private val cryptoCurrency: CryptoCurrency = savedStateHandle[TokenDetailsRouter.CRYPTO_CURRENCY_KEY] - ?: error("This screen can't open without CryptoCurrency") + ?: error("This screen can't open without `CryptoCurrency`") var router by Delegates.notNull() private val marketPriceJobHolder = JobHolder() private val refreshStateJobHolder = JobHolder() private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null - private var wallet by Delegates.notNull() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() @@ -91,23 +93,14 @@ internal class TokenDetailsViewModel @Inject constructor( private set override fun onCreate(owner: LifecycleOwner) { - getWallet() - updateContent(selectedWallet = wallet) + updateContent() handleBalanceHiding(owner) } - private fun getWallet() { - getSelectedWalletUseCase() - .fold( - ifLeft = { error("Can not get selected wallet $it") }, - ifRight = { wallet = it }, - ) - } - - private fun updateContent(selectedWallet: UserWallet) { - updateMarketPrice(selectedWallet = selectedWallet) + private fun updateContent() { + updateMarketPrice() updateTxHistory(refresh = false, showItemsLoading = true) - updateWarnings(selectedWallet = selectedWallet) + updateWarnings() } private fun handleBalanceHiding(owner: LifecycleOwner) { @@ -135,10 +128,10 @@ internal class TokenDetailsViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun updateWarnings(selectedWallet: UserWallet) { + private fun updateWarnings() { viewModelScope.launch(dispatchers.io) { getCurrencyWarningsUseCase.invoke( - userWalletId = selectedWallet.walletId, + userWalletId = userWalletId, currency = cryptoCurrency, derivationPath = cryptoCurrency.network.derivationPath, ) @@ -148,9 +141,9 @@ internal class TokenDetailsViewModel @Inject constructor( } } - private fun updateMarketPrice(selectedWallet: UserWallet) { + private fun updateMarketPrice() { getCurrencyStatusUpdatesUseCase( - userWalletId = selectedWallet.walletId, + userWalletId = userWalletId, currencyId = cryptoCurrency.id, derivationPath = cryptoCurrency.network.derivationPath, ) @@ -159,7 +152,7 @@ internal class TokenDetailsViewModel @Inject constructor( uiState = stateFactory.getCurrencyLoadedBalanceState(either) either.onRight { status -> cryptoCurrencyStatus = status - updateButtons(userWalletId = selectedWallet.walletId, currencyStatus = status) + updateButtons(userWalletId = userWalletId, currencyStatus = status) } } .flowOn(dispatchers.io) @@ -175,6 +168,7 @@ internal class TokenDetailsViewModel @Inject constructor( private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean) { viewModelScope.launch(dispatchers.io) { val txHistoryItemsCountEither = txHistoryItemsCountUseCase( + userWalletId = userWalletId, network = cryptoCurrency.network, ) @@ -184,9 +178,13 @@ internal class TokenDetailsViewModel @Inject constructor( } txHistoryItemsCountEither.onRight { - val either = txHistoryItemsUseCase(currency = cryptoCurrency) - .map { it.cachedIn(viewModelScope) } - uiState = stateFactory.getLoadedTxHistoryState(txHistoryEither = either) + val maybeTxHistory = txHistoryItemsUseCase( + userWalletId = userWalletId, + currency = cryptoCurrency, + refresh = refresh, + ).map { it.cachedIn(viewModelScope) } + + uiState = stateFactory.getLoadedTxHistoryState(maybeTxHistory) } } } @@ -211,13 +209,15 @@ internal class TokenDetailsViewModel @Inject constructor( analyticsEventsHandler.send(TokenScreenEvent.ButtonBuy(cryptoCurrency.symbol)) val status = cryptoCurrencyStatus ?: return - reduxStateHolder.dispatch( - TradeCryptoAction.New.Buy( - userWallet = wallet, - cryptoCurrencyStatus = status, - appCurrencyCode = selectedAppCurrencyFlow.value.code, - ), - ) + viewModelScope.launch(dispatchers.io) { + reduxStateHolder.dispatch( + TradeCryptoAction.New.Buy( + userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }, + cryptoCurrencyStatus = status, + appCurrencyCode = selectedAppCurrencyFlow.value.code, + ), + ) + } } override fun onReloadClick() { @@ -231,38 +231,38 @@ internal class TokenDetailsViewModel @Inject constructor( val cryptoCurrencyStatus = cryptoCurrencyStatus ?: return - when (cryptoCurrencyStatus.currency) { - is CryptoCurrency.Coin -> { - reduxStateHolder.dispatch( - action = TradeCryptoAction.New.SendCoin( - userWallet = wallet, - coinStatus = cryptoCurrencyStatus, - ), - ) + viewModelScope.launch(dispatchers.io) { + when (cryptoCurrencyStatus.currency) { + is CryptoCurrency.Coin -> { + reduxStateHolder.dispatch( + action = TradeCryptoAction.New.SendCoin( + userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }, + coinStatus = cryptoCurrencyStatus, + ), + ) + } + is CryptoCurrency.Token -> sendToken(status = cryptoCurrencyStatus) } - is CryptoCurrency.Token -> sendToken(status = cryptoCurrencyStatus) } } private fun sendToken(status: CryptoCurrencyStatus) { viewModelScope.launch(dispatchers.io) { - getNetworkCoinStatusUseCase( - userWalletId = wallet.walletId, + val maybeCoinStatus = getNetworkCoinStatusUseCase( + userWalletId = userWalletId, networkId = status.currency.network.id, derivationPath = status.currency.network.derivationPath, - ) - .take(count = 1) - .collectLatest { - it.onRight { coinStatus -> - reduxStateHolder.dispatch( - action = TradeCryptoAction.New.SendToken( - userWallet = wallet, - tokenStatus = status, - coinFiatRate = coinStatus.value.fiatRate, - ), - ) - } - } + ).firstOrNull() + + maybeCoinStatus?.onRight { coinStatus -> + reduxStateHolder.dispatch( + action = TradeCryptoAction.New.SendToken( + userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }, + tokenStatus = status, + coinFiatRate = coinStatus.value.fiatRate, + ), + ) + } } } @@ -271,7 +271,7 @@ internal class TokenDetailsViewModel @Inject constructor( viewModelScope.launch(dispatchers.io) { val addresses = walletManagersFacade.getAddress( - userWalletId = wallet.walletId, + userWalletId = userWalletId, network = cryptoCurrency.network, ) @@ -314,7 +314,7 @@ internal class TokenDetailsViewModel @Inject constructor( analyticsEventsHandler.send(TokenScreenEvent.ButtonRemoveToken(cryptoCurrency.symbol)) viewModelScope.launch { - val hasLinkedTokens = removeCurrencyUseCase.hasLinkedTokens(wallet.walletId, cryptoCurrency) + val hasLinkedTokens = removeCurrencyUseCase.hasLinkedTokens(userWalletId, cryptoCurrency) uiState = if (hasLinkedTokens) { stateFactory.getStateWithLinkedTokensDialog(cryptoCurrency) } else { @@ -325,7 +325,7 @@ internal class TokenDetailsViewModel @Inject constructor( override fun onHideConfirmed() { viewModelScope.launch { - removeCurrencyUseCase.invoke(wallet.walletId, cryptoCurrency) + removeCurrencyUseCase.invoke(userWalletId, cryptoCurrency) .onLeft { Timber.e(it) } .onRight { router.popBackStack() } } @@ -335,7 +335,7 @@ internal class TokenDetailsViewModel @Inject constructor( analyticsEventsHandler.send(TokenScreenEvent.ButtonExplore(cryptoCurrency.symbol)) viewModelScope.launch(dispatchers.io) { val addresses = walletManagersFacade.getAddress( - userWalletId = wallet.walletId, + userWalletId = userWalletId, network = cryptoCurrency.network, ) @@ -357,7 +357,7 @@ internal class TokenDetailsViewModel @Inject constructor( viewModelScope.launch { router.openUrl( url = getExploreUrlUseCase( - userWalletId = wallet.walletId, + userWalletId = userWalletId, network = cryptoCurrency.network, addressType = addressType, ), @@ -373,8 +373,8 @@ internal class TokenDetailsViewModel @Inject constructor( viewModelScope.launch(dispatchers.io) { listOf( async { - fetchCurrencyStatusUseCase.invoke( - userWalletId = wallet.walletId, + fetchCurrencyStatusUseCase( + userWalletId = userWalletId, id = cryptoCurrency.id, derivationPath = cryptoCurrency.network.derivationPath, refresh = true, @@ -386,7 +386,7 @@ internal class TokenDetailsViewModel @Inject constructor( showItemsLoading = uiState.txHistoryState !is TxHistoryState.Content, ) }, - async { updateWarnings(wallet) }, + async { updateWarnings() }, ).awaitAll() uiState = stateFactory.getRefreshedState() }.saveIn(refreshStateJobHolder) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 2c7205d680..c481e57703 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -107,11 +107,14 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr reduxNavController.navigate(action = NavigationAction.OpenUrl(url)) } - override fun openTokenDetails(currency: CryptoCurrency) { + override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { reduxNavController.navigate( action = NavigationAction.NavigateTo( screen = AppScreen.WalletDetails, - bundle = bundleOf(TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency), + bundle = bundleOf( + TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, + TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency, + ), ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index e48e601399..07a2e8a80b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -44,7 +44,7 @@ internal interface InnerWalletRouter : WalletRouter { fun openTxHistoryWebsite(url: String) /** Open token details screen */ - fun openTokenDetails(currency: CryptoCurrency) + fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) /** Open stories screen */ fun openStoriesScreen() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 9cb22da162..5b6e0fd44e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -726,7 +726,7 @@ internal class WalletViewModel @Inject constructor( override fun onTokenItemClick(currency: CryptoCurrency) { analyticsEventsHandler.send(PortfolioEvent.TokenTapped) - router.openTokenDetails(currency = currency) + router.openTokenDetails(getSelectedWallet().walletId, currency) } override fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { @@ -963,7 +963,7 @@ internal class WalletViewModel @Inject constructor( private fun getSingleCurrencyContent(index: Int) { val wallet = getWallet(index) - getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = wallet.walletId) + getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId) .distinctUntilChanged() .onEach { maybeCryptoCurrencyStatus -> uiState = stateFactory.getSingleCurrencyLoadedBalanceState(maybeCryptoCurrencyStatus) @@ -976,8 +976,8 @@ internal class WalletViewModel @Inject constructor( } updateNotifications(index) - updateButtons(userWalletId = wallet.walletId, currencyStatus = status) - updateTxHistory(status.currency) + updateButtons(wallet.walletId, status) + updateTxHistory(wallet.walletId, status.currency, refresh = false) } } .flowOn(dispatchers.io) @@ -985,9 +985,12 @@ internal class WalletViewModel @Inject constructor( .saveIn(marketPriceJobHolder) } - private fun updateTxHistory(currency: CryptoCurrency) { + private fun updateTxHistory(userWalletId: UserWalletId, currency: CryptoCurrency, refresh: Boolean) { viewModelScope.launch(dispatchers.io) { - val txHistoryItemsCountEither = txHistoryItemsCountUseCase(currency.network) + val txHistoryItemsCountEither = txHistoryItemsCountUseCase( + userWalletId = userWalletId, + network = currency.network, + ) uiState = stateFactory.getLoadingTxHistoryState( itemsCountEither = txHistoryItemsCountEither, @@ -995,7 +998,11 @@ internal class WalletViewModel @Inject constructor( txHistoryItemsCountEither.onRight { uiState = stateFactory.getLoadedTxHistoryState( - txHistoryEither = txHistoryItemsUseCase(currency = currency).map { + txHistoryEither = txHistoryItemsUseCase( + userWalletId = userWalletId, + currency = currency, + refresh = refresh, + ).map { it.cachedIn(viewModelScope) }, ) @@ -1057,6 +1064,10 @@ internal class WalletViewModel @Inject constructor( uiState = stateFactory.getRefreshedState() uiState = result.fold(stateFactory::getStateByCurrencyStatusError) { uiState } + + singleWalletCryptoCurrencyStatus?.let { + updateTxHistory(wallet.walletId, it.currency, refresh = true) + } }.saveIn(refreshContentJobHolder) } @@ -1091,5 +1102,12 @@ internal class WalletViewModel @Inject constructor( ) } + private fun getSelectedWallet(): UserWallet { + val state = uiState as? WalletState.ContentState + ?: error("Unable to get selected user wallet") + + return getWallet(state.walletsListConfig.selectedWalletIndex) + } + private fun getCardTypeResolver(index: Int): CardTypesResolver = getWallet(index).scanResponse.cardTypesResolver } \ No newline at end of file From 8962f8d333186237a169c8a4652c7464052c4cd9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Oct 2023 12:07:39 +0500 Subject: [PATCH 04/12] Updated on 2026-08-14 --- .../ui/components/AddCustomTokenForm.kt | 4 +- .../details/ui/details/DetailsScreen.kt | 2 +- .../ui/components/SaveWalletScreenContent.kt | 2 +- .../wallet/ui/view/TotalBalanceCard.kt | 2 +- .../ui/components/WalletItem.kt | 4 +- .../tangem/core/ui/components/TextFields.kt | 5 +- .../ui/components/appbar/AppBarWithSearch.kt | 2 +- .../tokenreceive/TokenReceiveBottomSheet.kt | 2 +- .../components/notifications/Notification.kt | 2 +- .../ui/components/transactions/Transaction.kt | 4 +- .../com/tangem/core/ui/res/TangemTheme.kt | 9 ++-- .../tangem/core/ui/res/TangemTypography.kt | 50 ++++++++++++++----- .../presentation/ui/GetBonusView.kt | 5 +- .../ui/Learn2earnStoriesScreen.kt | 3 +- .../wallet2/ui/ImportSeedPhraseScreen.kt | 2 +- .../feature/referral/ui/AgreementText.kt | 2 +- .../feature/swap/ui/SwapSelectTokenScreen.kt | 6 +-- .../TokenDetailsLoadedBalanceConverter.kt | 26 +++++----- .../ui/components/TokenDetailsBalanceBlock.kt | 4 +- .../ui/components/TokenInfoBlock.kt | 8 +-- .../component/token/NonFiatContentBlock.kt | 3 +- .../component/token/TokenCryptoAmount.kt | 3 +- .../common/component/token/TokenFiatAmount.kt | 3 +- .../component/token/TokenPriceChange.kt | 3 +- .../common/component/token/TokenTitle.kt | 3 +- .../wallet/ui/components/common/WalletCard.kt | 2 +- .../multicurrency/MultiCurrencyContent.kt | 2 +- 27 files changed, 87 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenForm.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenForm.kt index 0090ef8223..fb6c260728 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenForm.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenForm.kt @@ -94,7 +94,7 @@ private fun TextField(model: AddCustomTokenInputField, isError: Boolean) { label = { Text( text = model.label.resolveReference(), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTextFieldsDefault.defaultTextFieldColors.labelColor( enabled = isEnabled, error = isError, @@ -178,7 +178,7 @@ private fun SelectorField(model: AddCustomTokenSelectorField) { text = subtitle, color = TangemTheme.colors.text.secondary, maxLines = 1, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt index 1d074d274a..8a65d0e7bd 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt @@ -229,7 +229,7 @@ private fun TangemAppVersion(appNameRes: Int, version: String, modifier: Modifie Text( modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16), text = "${stringResource(id = appNameRes)} $version", - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/ui/components/SaveWalletScreenContent.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/ui/components/SaveWalletScreenContent.kt index a20a0edf59..a8bbc46f02 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/ui/components/SaveWalletScreenContent.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/ui/components/SaveWalletScreenContent.kt @@ -144,7 +144,7 @@ private fun Footer(showProgress: Boolean, onSaveWalletClick: () -> Unit) { Text( modifier = Modifier.fillMaxWidth(fraction = .7f), text = stringResource(R.string.save_user_wallet_agreement_notice), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.Center, ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt index fdfadc43e1..d39bf43e05 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt @@ -134,7 +134,7 @@ private fun TotalBalanceCardContent(state: TotalBalanceCardState, modifier: Modi Text( modifier = Modifier.fillMaxWidth(), text = stringResource(id = R.string.main_processing_full_amount), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.attention, ) } diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt index 62ef2c0468..0224ee04fc 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt @@ -151,7 +151,7 @@ private fun RowScope.WalletInfo(wallet: UserWalletItem, isSelected: Boolean) { }, color = TangemTheme.colors.text.tertiary, maxLines = 1, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } } @@ -255,7 +255,7 @@ private fun LoadedTokensInfo( count = tokensCount, tokensCount, ), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.End, ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/TextFields.kt b/core/ui/src/main/java/com/tangem/core/ui/components/TextFields.kt index 6f09757064..e954cbd32d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/TextFields.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/TextFields.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography /** * [Show in Figma](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=213%3A218&t=TmfD6UBHPg9uYfev-4) @@ -103,7 +102,7 @@ private fun TangemTextField( if (!label.isNullOrEmpty()) { Text( text = label, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = colors.labelColor( enabled = enabled, error = isError, @@ -161,7 +160,7 @@ private fun TangemTextField( .fillMaxWidth() .padding(horizontal = 16.dp), text = caption, - style = TangemTypography.body1, + style = TangemTheme.typography.body1, color = colors.captionColor(enabled = enabled, isError = isError).value, ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt index 55b0a08fe3..b88991e655 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithSearch.kt @@ -169,7 +169,7 @@ private fun SubtitleView(subtitle: String, icon: Painter?) { text = subtitle, color = TangemTheme.colors.text.secondary, maxLines = 1, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt index ee8b8d3581..ce07dc5ba7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt @@ -63,7 +63,7 @@ private fun TokenReceiveBottomSheetContent(content: TokenReceiveBottomSheetConfi text = stringResource(R.string.receive_bottom_sheet_warning_message_full, content.name), color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) Row( horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt index 6e569c56bb..56b449ec3a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/Notification.kt @@ -157,7 +157,7 @@ private fun TextsBlock(title: TextReference, subtitle: TextReference) { Text( text = subtitle.resolveReference(), color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt index 283bbf7b89..4cbbb22026 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -251,7 +251,7 @@ private fun Subtitle(state: TransactionState, modifier: Modifier = Modifier) { modifier = modifier, textAlign = TextAlign.Start, color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } is TransactionState.Loading -> { @@ -304,7 +304,7 @@ private fun Timestamp(state: TransactionState, modifier: Modifier = Modifier) { modifier = modifier, textAlign = TextAlign.End, color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } is TransactionState.Loading -> { diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index 293956cef6..a4f6721f5c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -3,7 +3,6 @@ package com.tangem.core.ui.res import androidx.compose.material.Colors import androidx.compose.material.MaterialTheme import androidx.compose.material.ProvideTextStyle -import androidx.compose.material.Typography import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.ReadOnlyComposable @@ -17,7 +16,7 @@ internal const val IS_SYSTEM_IN_DARK_THEME: Boolean = false @Composable fun TangemTheme( isDark: Boolean = false, - typography: Typography = TangemTheme.typography, + typography: TangemTypography = TangemTheme.typography, dimens: TangemDimens = TangemTheme.dimens, content: @Composable () -> Unit, ) { @@ -26,10 +25,8 @@ fun TangemTheme( .also { it.update(themeColors) } val shapes = remember { TangemShapes(dimens) } - MaterialTheme( colors = materialThemeColors(colors = themeColors, isDark = isDark), - typography = typography, ) { CompositionLocalProvider( LocalTangemColors provides rememberedColors, @@ -52,7 +49,7 @@ object TangemTheme { @ReadOnlyComposable get() = LocalTangemColors.current - val typography: Typography + val typography: TangemTypography @Composable @ReadOnlyComposable get() = LocalTangemTypography.current @@ -197,7 +194,7 @@ private val LocalTangemColors = staticCompositionLocalOf { } private val LocalTangemTypography = staticCompositionLocalOf { - TangemTypography + TangemTypography() } private val LocalTangemDimens = staticCompositionLocalOf { diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTypography.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTypography.kt index bdcd413dbd..0187255098 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTypography.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTypography.kt @@ -1,6 +1,6 @@ package com.tangem.core.ui.res -import androidx.compose.material.Typography +import androidx.compose.runtime.Immutable import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily @@ -15,63 +15,87 @@ private val RobotoFamily = FontFamily( Font(R.font.roboto_medium, FontWeight.Medium), ) -val TangemTypography = Typography( - defaultFontFamily = RobotoFamily, - h1 = TextStyle( +@Immutable +data class TangemTypography internal constructor( + val head: TextStyle = TextStyle( + fontFamily = RobotoFamily, + fontSize = 34.sp, + fontWeight = FontWeight.SemiBold, + letterSpacing = TextUnit(value = 0f, type = TextUnitType.Sp), + lineHeight = TextUnit(value = 44f, type = TextUnitType.Sp), + ), + val h1: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 34.sp, fontWeight = FontWeight.Normal, letterSpacing = TextUnit(value = 0f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 44f, type = TextUnitType.Sp), ), - h2 = TextStyle( + val h2: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 24.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 0.18f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 32f, type = TextUnitType.Sp), ), - h3 = TextStyle( + val h3: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 20.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 0.15f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 24f, type = TextUnitType.Sp), ), - subtitle1 = TextStyle( + val subtitle1: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 16.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 0.15f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 24f, type = TextUnitType.Sp), ), - subtitle2 = TextStyle( + val subtitle2: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 14.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 0.5f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 24f, type = TextUnitType.Sp), ), - body1 = TextStyle( + val body1: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 16.sp, fontWeight = FontWeight.Normal, letterSpacing = TextUnit(value = 0.5f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 24f, type = TextUnitType.Sp), ), - body2 = TextStyle( + val body2: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 14.sp, fontWeight = FontWeight.Normal, letterSpacing = TextUnit(value = 0.25f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 20f, type = TextUnitType.Sp), ), - button = TextStyle( + val button: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 14.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 0.1f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 16f, type = TextUnitType.Sp), ), - caption = TextStyle( + val caption1: TextStyle = TextStyle( + fontFamily = RobotoFamily, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + letterSpacing = TextUnit(value = 0.4f, type = TextUnitType.Sp), + lineHeight = TextUnit(value = 16f, type = TextUnitType.Sp), + ), + val caption2: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 12.sp, fontWeight = FontWeight.Normal, letterSpacing = TextUnit(value = 0.4f, type = TextUnitType.Sp), lineHeight = TextUnit(value = 16f, type = TextUnitType.Sp), ), - overline = TextStyle( + val overline: TextStyle = TextStyle( + fontFamily = RobotoFamily, fontSize = 10.sp, fontWeight = FontWeight.Medium, letterSpacing = TextUnit(value = 1.5f, type = TextUnitType.Sp), diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusView.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusView.kt index a36d44f760..792105fb40 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusView.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/GetBonusView.kt @@ -22,7 +22,6 @@ import com.tangem.core.ui.components.SpacerH4 import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.learn2earn.impl.R import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle import com.tangem.feature.learn2earn.presentation.ui.state.MainScreenState @@ -70,13 +69,13 @@ internal fun GetBonusView(state: MainScreenState, modifier: Modifier = Modifier) ) { Text( text = state.description.title.resolveReference(), - style = TangemTypography.body1, + style = TangemTheme.typography.body1, color = TangemTheme.colors.text.primary2, ) SpacerH4() Text( text = state.description.subtitle.resolveReference(), - style = TangemTypography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnStoriesScreen.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnStoriesScreen.kt index b91ffaf2f5..4d498fd377 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnStoriesScreen.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnStoriesScreen.kt @@ -17,7 +17,6 @@ import com.tangem.core.ui.components.SecondaryButton import com.tangem.core.ui.components.SpacerH16 import com.tangem.core.ui.components.SpacerH32 import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.learn2earn.impl.R import com.tangem.feature.learn2earn.presentation.ui.component.GradientCircle @@ -106,7 +105,7 @@ private fun StoryDescription(headerText: String, bodyText: String, modifier: Mod Text( text = bodyText, textAlign = TextAlign.Center, - style = TangemTypography.subtitle1, + style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.tertiary, ) } diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt index 5f9b14ae11..10248b767a 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/ui/ImportSeedPhraseScreen.kt @@ -112,7 +112,7 @@ private fun PhraseBlock(state: ImportSeedPhraseState, modifier: Modifier = Modif Text( modifier = Modifier.fillMaxSize(), text = message, - style = TangemTheme.typography.caption.copy( + style = TangemTheme.typography.caption2.copy( color = TangemTheme.colors.text.warning, ), ) diff --git a/features/referral/presentation/src/main/java/com/tangem/feature/referral/ui/AgreementText.kt b/features/referral/presentation/src/main/java/com/tangem/feature/referral/ui/AgreementText.kt index c143b3b4e5..d29a6c996b 100644 --- a/features/referral/presentation/src/main/java/com/tangem/feature/referral/ui/AgreementText.kt +++ b/features/referral/presentation/src/main/java/com/tangem/feature/referral/ui/AgreementText.kt @@ -26,7 +26,7 @@ internal fun AgreementText(@StringRes firstPartResId: Int, onClick: () -> Unit) modifier = Modifier .fillMaxWidth() .padding(horizontal = TangemTheme.dimens.spacing54), - style = TangemTheme.typography.caption.copy(textAlign = TextAlign.Center), + style = TangemTheme.typography.caption2.copy(textAlign = TextAlign.Center), maxLines = 2, onClick = { val clickableSpanStyle = requireNotNull(agreementText.spanStyles.getOrNull(1)) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index eb086a8df4..23647ff1d8 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -150,7 +150,7 @@ private fun TokenItem(token: TokenToSelect, network: Network, onTokenClick: () - SpacerW2() Text( text = token.symbol, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } @@ -160,7 +160,7 @@ private fun TokenItem(token: TokenToSelect, network: Network, onTokenClick: () - if (!token.available) { Text( text = stringResource(id = R.string.swapping_token_not_available), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } else if (token.addedTokenBalanceData != null) { @@ -177,7 +177,7 @@ private fun TokenItem(token: TokenToSelect, network: Network, onTokenClick: () - SpacerW2() Text( text = token.addedTokenBalanceData.amount.orEmpty(), - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 9240092123..cf82bb5725 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -59,24 +59,22 @@ internal class TokenDetailsLoadedBalanceConverter( return when (status.value) { is CryptoCurrencyStatus.NoQuote, is CryptoCurrencyStatus.Loaded, - -> { - TokenDetailsBalanceBlockState.Content( - actionButtons = currentState.actionButtons, - fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()), - cryptoBalance = formatCryptoAmount(status), - ) - } - is CryptoCurrencyStatus.Loading -> { - TokenDetailsBalanceBlockState.Loading(currentState.actionButtons) - } + -> TokenDetailsBalanceBlockState.Content( + actionButtons = currentState.actionButtons, + fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()), + cryptoBalance = formatCryptoAmount(status), + ) + is CryptoCurrencyStatus.NoAccount -> TokenDetailsBalanceBlockState.Content( + actionButtons = currentState.actionButtons, + fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()), + cryptoBalance = formatCryptoAmount(status), + ) + is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading(currentState.actionButtons) is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.NoAccount, is CryptoCurrencyStatus.Custom, is CryptoCurrencyStatus.NoAmount, is CryptoCurrencyStatus.Unreachable, - -> { - TokenDetailsBalanceBlockState.Error(currentState.actionButtons) - } + -> TokenDetailsBalanceBlockState.Error(currentState.actionButtons) } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index 0fc3606d9b..2df27ff04c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -112,13 +112,13 @@ private fun CryptoBalance( is TokenDetailsBalanceBlockState.Content -> Text( modifier = modifier, text = if (isBalanceHidden) STARS else state.cryptoBalance, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) is TokenDetailsBalanceBlockState.Error -> Text( modifier = modifier, text = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt index 4d149f38c8..7ccdf694b5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt @@ -34,7 +34,7 @@ internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Mod ) { Text( text = state.name, - style = TangemTheme.typography.h1, + style = TangemTheme.typography.head, color = TangemTheme.colors.text.primary1, ) NetworkInfoText(state.currency) @@ -63,7 +63,7 @@ private fun NetworkInfoText(currency: TokenInfoBlockState.Currency) { Text( text = stringResource(id = R.string.common_main_network), color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } is TokenInfoBlockState.Currency.Token -> { @@ -74,7 +74,7 @@ private fun NetworkInfoText(currency: TokenInfoBlockState.Currency) { val state = extractNetwork(tokenCurrency = currency) Text( text = state.normalText, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) Icon( @@ -85,7 +85,7 @@ private fun NetworkInfoText(currency: TokenInfoBlockState.Currency) { ) Text( text = state.boldText, - style = TangemTheme.typography.caption.copy(fontWeight = FontWeight.Medium), + style = TangemTheme.typography.caption2.copy(fontWeight = FontWeight.Medium), color = TangemTheme.colors.text.primary1, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt index cb5c920555..478991676d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/NonFiatContentBlock.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import org.burnoutcrew.reorderable.ReorderableLazyListState @@ -73,6 +72,6 @@ private fun NonFiatContentText(@StringRes text: Int) { color = TangemTheme.colors.text.tertiary, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = TangemTypography.body2, + style = TangemTheme.typography.body2, ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt index 74d1cf836f..426d6d153a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenCryptoAmount.kt @@ -11,7 +11,6 @@ import androidx.compose.ui.text.style.TextOverflow import com.tangem.common.Strings import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState.CryptoAmountState as TokenCryptoAmountState @@ -49,7 +48,7 @@ private fun CryptoAmountText(amount: String, modifier: Modifier = Modifier) { color = TangemTheme.colors.text.tertiary, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = TangemTypography.body2, + style = TangemTheme.typography.body2, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt index 6aff7c7e1c..f223ecdcfe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenFiatAmount.kt @@ -10,7 +10,6 @@ import androidx.compose.ui.text.style.TextOverflow import com.tangem.common.Strings import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.wallet.presentation.common.state.TokenItemState.FiatAmountState as TokenFiatAmountState @Composable @@ -40,7 +39,7 @@ private fun FiatAmountText(text: String, modifier: Modifier = Modifier) { color = TangemTheme.colors.text.primary1, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = TangemTypography.body2, + style = TangemTheme.typography.body2, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt index 002357512e..d40de53a30 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenPriceChange.kt @@ -15,7 +15,6 @@ import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.SpacerW4 import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.PriceChangeState as TokenPriceChangeState @@ -87,7 +86,7 @@ private fun PriceChangeText(type: PriceChangeType?, text: String?) { }, overflow = TextOverflow.Ellipsis, maxLines = 1, - style = TangemTypography.body2, + style = TangemTheme.typography.body2, ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt index 85091af4f8..e5b3050a53 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/token/TokenTitle.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemTypography import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TitleState as TokenTitleState @@ -63,7 +62,7 @@ private fun CurrencyNameText(name: String, modifier: Modifier = Modifier) { color = TangemTheme.colors.text.primary1, overflow = TextOverflow.Ellipsis, maxLines = 1, - style = TangemTypography.subtitle2, + style = TangemTheme.typography.subtitle2, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 9af63cffb8..f0348827c3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -361,7 +361,7 @@ private fun AdditionalInfoText(text: TextReference) { color = TangemTheme.colors.text.tertiary, maxLines = 1, overflow = TextOverflow.Ellipsis, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 9bdaf10b43..287f84f8d9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -82,7 +82,7 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) { modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.Center, - style = TangemTheme.typography.caption, + style = TangemTheme.typography.caption2, ) } } From 965a6f8e4610c463994159553cd2ed11c1896ed6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 16:04:17 +0300 Subject: [PATCH 05/12] Updated on 2026-08-14 --- .../kotlin/com/tangem/data/tokens/utils/TokensOperations.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index d4549a6243..be82d11c84 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -65,8 +65,8 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? { internal fun List.hasCoinForToken(token: CryptoCurrency.Token): Boolean { return any { val blockchain = getBlockchain(networkId = token.network.id) - - it.id == blockchain.toCoinId() + val tokenDerivation = token.network.derivationPath.value + it.id == blockchain.toCoinId() && it.derivationPath == tokenDerivation } } From a78432b9f619743ab1f8ef647f12def7e6701aa4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 10 Oct 2023 16:20:40 +0800 Subject: [PATCH 06/12] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 8 ++++++- .../common/state/TokenItemState.kt | 8 ++++--- .../state/components/WalletCardState.kt | 15 ++++++++----- .../factory/WalletSkeletonStateConverter.kt | 1 + .../wallet/ui/components/common/WalletCard.kt | 12 +---------- ...ryptoCurrencyStatusToTokenItemConverter.kt | 13 +++++++++--- .../utils/FiatBalanceToWalletCardConverter.kt | 2 +- .../wallet/utils/LoadingItemsProvider.kt | 21 ------------------- 8 files changed, 35 insertions(+), 45 deletions(-) delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 9456466cb9..c22554ba8d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -207,7 +207,13 @@ internal object WalletPreviewData { ) } - val loadingTokenItemState by lazy { TokenItemState.Loading(id = "Loading#1") } + val loadingTokenItemState by lazy { + TokenItemState.Loading( + id = "Loading#1", + iconState = customTokenIconState.copy(isGrayscale = true), + titleState = TokenItemState.TitleState.Content(text = "Polygon"), + ) + } private const val networksSize = 10 private const val tokensSize = 3 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt index b5d6b52d47..2002ca9fc9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt @@ -22,9 +22,11 @@ internal sealed class TokenItemState { abstract val priceChangeState: PriceChangeState? /** Loading token state */ - data class Loading(override val id: String) : TokenItemState() { - override val iconState: IconState = IconState.Loading - override val titleState: TitleState = TitleState.Loading + data class Loading( + override val id: String, + override val iconState: IconState, + override val titleState: TitleState.Content, + ) : TokenItemState() { override val fiatAmountState: FiatAmountState = FiatAmountState.Loading override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Loading override val priceChangeState: PriceChangeState = PriceChangeState.Loading diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index 8edccf6a0f..b1d0bb3853 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -16,6 +16,8 @@ internal sealed interface WalletCardState { /** Title */ val title: String + val additionalInfo: TextReference? + /** Wallet image resource id */ @get:DrawableRes val imageResId: Int? @@ -41,10 +43,10 @@ internal sealed interface WalletCardState { data class Content( override val id: UserWalletId, override val title: String, + override val additionalInfo: TextReference, override val imageResId: Int?, override val onRenameClick: (UserWalletId, String) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, - val additionalInfo: TextReference, val cardCount: Int?, val balance: String, ) : WalletCardState @@ -64,10 +66,10 @@ internal sealed interface WalletCardState { data class HiddenContent( override val id: UserWalletId, override val title: String, + override val additionalInfo: TextReference, override val imageResId: Int?, override val onRenameClick: (UserWalletId, String) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, - val additionalInfo: TextReference, val balance: String, val cardCount: Int?, ) : WalletCardState @@ -77,18 +79,18 @@ internal sealed interface WalletCardState { * * @property id wallet id * @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 additionalInfo wallet additional info */ data class LockedContent( override val id: UserWalletId, override val title: String, + override val additionalInfo: TextReference, override val imageResId: Int?, override val onRenameClick: (UserWalletId, String) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, - val additionalInfo: TextReference, ) : WalletCardState /** @@ -106,7 +108,9 @@ internal sealed interface WalletCardState { override val imageResId: Int?, override val onRenameClick: (UserWalletId, String) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, - ) : WalletCardState + ) : WalletCardState { + override val additionalInfo: TextReference = EMPTY_BALANCE_TEXT + } /** * Wallet card loading state @@ -120,6 +124,7 @@ internal sealed interface WalletCardState { data class Loading( override val id: UserWalletId, override val title: String, + override val additionalInfo: TextReference? = null, override val imageResId: Int?, override val onRenameClick: (UserWalletId, String) -> Unit, override val onDeleteClick: (UserWalletId) -> Unit, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 51a78e138e..df44339b4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -126,6 +126,7 @@ internal class WalletSkeletonStateConverter( return WalletCardState.Loading( id = walletId, title = name, + additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null, imageResId = createImageResId(), onRenameClick = clickIntents::onRenameClick, onDeleteClick = clickIntents::onDeleteClick, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index f0348827c3..fb0002479c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -93,7 +93,7 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { ) AdditionalInfo( - text = resolveAdditionalTextByState(state), + text = state.additionalInfo, modifier = Modifier.constrainAs(additionalTextRef) { start.linkTo(parent.start) top.linkTo(balanceRef.bottom) @@ -344,16 +344,6 @@ private fun AdditionalInfo(text: TextReference?, modifier: Modifier = Modifier) } } -private fun resolveAdditionalTextByState(state: WalletCardState): TextReference? { - return when (state) { - is WalletCardState.Content -> state.additionalInfo - is WalletCardState.LockedContent -> state.additionalInfo - is WalletCardState.Error -> WalletCardState.EMPTY_BALANCE_TEXT - is WalletCardState.HiddenContent -> WalletCardState.HIDDEN_BALANCE_TEXT - is WalletCardState.Loading -> null - } -} - @Composable private fun AdditionalInfoText(text: TextReference) { Text( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 89ce3ee6b1..c23c0cefa7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -17,24 +17,31 @@ internal class CryptoCurrencyStatusToTokenItemConverter( private val clickIntents: WalletClickIntents, ) : Converter { - private val iconStateConverter = CryptoCurrencyToIconStateConverter() + private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) override fun convert(value: CryptoCurrencyStatus): TokenItemState { return when (value.value) { - is CryptoCurrencyStatus.Loading -> TokenItemState.Loading(id = value.currency.id.value) + is CryptoCurrencyStatus.Loading -> value.mapToLoadingState() is CryptoCurrencyStatus.Loaded, is CryptoCurrencyStatus.Custom, is CryptoCurrencyStatus.NoQuote, is CryptoCurrencyStatus.NoAccount, -> value.mapToTokenItemState() is CryptoCurrencyStatus.MissedDerivation -> value.mapToNoAddressTokenItemState() - // TODO: Add other token item states, currently not designed is CryptoCurrencyStatus.Unreachable, is CryptoCurrencyStatus.NoAmount, -> value.mapToUnreachableTokenItemState() } } + private fun CryptoCurrencyStatus.mapToLoadingState(): TokenItemState.Loading { + return TokenItemState.Loading( + id = currency.id.value, + iconState = iconStateConverter.convert(value = this), + titleState = TokenItemState.TitleState.Content(text = currency.name), + ) + } + private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content { return TokenItemState.Content( id = currency.id.value, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index bca1082c42..6be00960a7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -26,7 +26,7 @@ internal class FiatBalanceToWalletCardConverter( } private fun WalletCardState.toLoadingWalletCardState(): WalletCardState { - return WalletCardState.Loading(id, title, imageResId, onRenameClick, onDeleteClick) + return WalletCardState.Loading(id, title, additionalInfo, imageResId, onRenameClick, onDeleteClick) } private fun WalletCardState.toErrorWalletCardState(): WalletCardState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt deleted file mode 100644 index c7e545dec3..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -internal object LoadingItemsProvider { - - fun getLoadingMultiCurrencyTokens(): ImmutableList { - val items = mutableListOf() - repeat(times = 5) { - items.add( - WalletTokensListState.TokensListItemState.Token( - state = TokenItemState.Loading(id = "Loading#$it"), - ), - ) - } - return items.toImmutableList() - } -} \ No newline at end of file From 7d6e48a613cba21a034315218117e4ae7d314cf4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 18:44:27 +0300 Subject: [PATCH 07/12] Updated on 2026-08-14 --- .../presentation/organizetokens/OrganizeTokensScreen.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 63b4037673..6053795ce3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -19,6 +19,8 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -84,6 +86,7 @@ private fun TokenList( dndConfig: OrganizeTokensState.DragAndDropConfig, modifier: Modifier = Modifier, ) { + val hapticFeedback = LocalHapticFeedback.current Box(modifier = modifier) { val onDragEnd: (Int, Int) -> Unit = remember { { _, _ -> @@ -117,7 +120,10 @@ private fun TokenList( ) { index, item -> val onDragStart = remember(item) { - { dndConfig.onItemDragStart(item) } + { + dndConfig.onItemDragStart(item) + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + } } DraggableItem( From 648236f1870a9d247f85bea8c1173bc9dc848c75 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 17:05:59 +0300 Subject: [PATCH 08/12] Updated on 2026-08-14 --- .../tokenreceive/TokenReceiveBottomSheet.kt | 13 ++++++++----- .../presentation/router/DefaultWalletRouter.kt | 2 +- gradle/dependencies.toml | 16 ++++++++-------- .../plugin/configuration/model/AppConfig.kt | 4 ++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt index ce07dc5ba7..417b9fcb35 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/tokenreceive/TokenReceiveBottomSheet.kt @@ -100,15 +100,18 @@ private fun TokenReceiveBottomSheetContent(content: TokenReceiveBottomSheetConfi @Composable private fun QrCodeContent(content: TokenReceiveBottomSheetConfig, onAddressChange: (AddressModel) -> Unit) { val qrCodes = rememberQrPainters(content.addresses.map(AddressModel::value)) - val pagerState = rememberPagerState() - val pageCount = content.addresses.count() + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f, + ) { + content.addresses.count() + } LaunchedEffect(key1 = pagerState.currentPage) { onAddressChange.invoke(content.addresses[pagerState.currentPage]) } HorizontalPager( - pageCount = pageCount, state = pagerState, ) { currentPage -> Column( @@ -142,7 +145,7 @@ private fun QrCodeContent(content: TokenReceiveBottomSheetConfig, onAddressChang } } - if (pageCount > 1) { + if (pagerState.pageCount > 1) { val indicatorState = rememberLazyListState() val selectedColor = TangemTheme.colors.icon.primary1 val unselectedColor = TangemTheme.colors.icon.informative @@ -153,7 +156,7 @@ private fun QrCodeContent(content: TokenReceiveBottomSheetConfig, onAddressChang horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - repeat(pageCount) { iteration -> + repeat(pagerState.pageCount) { iteration -> item(key = iteration) { val color = if (pagerState.currentPage == iteration) { selectedColor diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index c481e57703..6ad00bcf52 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -78,7 +78,7 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr * next element is wallet screen entry. * If backstack contains only NavGraph entry and wallet screen entry then we close the wallet fragment. */ - if (navController.backQueue.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) { + if (navController.currentBackStack.value.size == BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN) { if (screen != null) { reduxNavController.navigate(action = NavigationAction.PopBackTo(screen)) } else { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index f0b0c1d42f..a75f75bdfa 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -10,7 +10,7 @@ kotlin = "1.8.21" # endregion Classpath # region AndroidX -androidxActivityCompose = "1.5.0" +androidxActivityCompose = "1.8.0" androidxAppCompat = "1.5.1" androidxBrowser = "1.4.0" androidxConstraintLayout = "2.1.4" @@ -24,16 +24,16 @@ androidx-palette = "1.0.0" # region Compose compose-compiler = "1.4.7" -compose-runtime = "1.4.3" -compose-foundation = "1.4.3" -compose-material = "1.4.3" -compose-material3 = "1.1.0" +compose-runtime = "1.5.3" +compose-foundation = "1.5.3" +compose-material = "1.5.3" +compose-material3 = "1.1.2" compose-constraint = "1.0.1" -compose-navigation = "2.5.3" +compose-navigation = "2.7.4" compose-accompanist = "0.30.1" -compose-paging = "3.2.0" +compose-paging = "3.2.1" compose-reorderable = "0.9.6" -compoese-lifecycle-runtime = "2.6.1" +compoese-lifecycle-runtime = "2.6.2" # endregion Compose # region Other libraries diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt index 39b4f01925..c8bfbff0f1 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt @@ -5,6 +5,6 @@ internal object AppConfig { const val versionCode = 1 const val versionName = "1.0.0-SNAPSHOT" const val minSdkVersion = 23 - const val targetSdkVersion = 33 - const val compileSdkVersion = 33 + const val targetSdkVersion = 34 + const val compileSdkVersion = 34 } \ No newline at end of file From 7ce0d44c0aaec3f63d63b98ee37e8ff741d76c93 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 17:59:06 +0300 Subject: [PATCH 09/12] Updated on 2026-08-14 --- .../tangem/core/ui/components/buttons/HorizontalActionChips.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt index b34aa9c970..3a5bbafce5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt @@ -31,9 +31,9 @@ fun HorizontalActionChips( verticalAlignment = Alignment.CenterVertically, contentPadding = contentPadding, ) { + // do not use key cause when change items order, list is scrolled items( items = buttons, - key = { config -> config.text.hashCode() }, itemContent = { ActionButton(config = it) }, ) } From c2edc435d0dcb124ea1395f4b76e3a83949bfb58 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 13:28:43 +0300 Subject: [PATCH 10/12] Updated on 2026-08-14 --- .../wallet/ui/OnboardingWalletFragment.kt | 5 ++++- .../onboarding/di/OnboardingRouterModule.kt | 20 +++++++++++++++++++ .../navigation/DefaultOnboardingRouter.kt | 3 +++ .../onboarding/navigation/OnboardingRouter.kt | 12 +++++++++++ features/wallet/impl/build.gradle.kts | 3 +++ .../router/DefaultWalletRouter.kt | 8 +++++++- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 features/onboarding/src/main/java/com/tangem/feature/onboarding/di/OnboardingRouterModule.kt create mode 100644 features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/DefaultOnboardingRouter.kt create mode 100644 features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index be6b6cd967..2eb4abdc18 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -24,6 +24,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.core.ui.extensions.setStatusBarColor import com.tangem.domain.common.util.cardTypesResolver import com.tangem.feature.onboarding.data.model.CreateWalletResponse +import com.tangem.feature.onboarding.navigation.OnboardingRouter import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseMediator import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseRouter @@ -62,6 +63,8 @@ class OnboardingWalletFragment : internal val bindingSeedPhrase: LayoutOnboardingSeedPhraseBinding by lazy { binding.onboardingSeedPhraseContainer } + private val canSkipBackup by lazy { arguments?.getBoolean(OnboardingRouter.CAN_SKIP_BACKUP) ?: true } + private val seedPhraseStateHandler: OnboardingSeedPhraseStateHandler = OnboardingSeedPhraseStateHandler() private val seedPhraseViewModel by viewModels() @@ -255,7 +258,7 @@ class OnboardingWalletFragment : btnWalletAlternativeAction.text = getText(R.string.onboarding_button_skip_backup) btnWalletAlternativeAction.setOnClickListener { store.dispatch(BackupAction.SkipBackup) } - btnWalletAlternativeAction.show(state.canSkipBackup) + btnWalletAlternativeAction.show(state.canSkipBackup && canSkipBackup) } animator.showBackupIntro(state) } diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/di/OnboardingRouterModule.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/di/OnboardingRouterModule.kt new file mode 100644 index 0000000000..acc54e7b9b --- /dev/null +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/di/OnboardingRouterModule.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.onboarding.di + +import com.tangem.feature.onboarding.navigation.DefaultOnboardingRouter +import com.tangem.feature.onboarding.navigation.OnboardingRouter +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.scopes.ActivityScoped + +@Module +@InstallIn(ActivityComponent::class) +internal object OnboardingRouterModule { + + @Provides + @ActivityScoped + fun provideOnboardingRouter(): OnboardingRouter { + return DefaultOnboardingRouter() + } +} \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/DefaultOnboardingRouter.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/DefaultOnboardingRouter.kt new file mode 100644 index 0000000000..0d34ac7cd3 --- /dev/null +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/DefaultOnboardingRouter.kt @@ -0,0 +1,3 @@ +package com.tangem.feature.onboarding.navigation + +class DefaultOnboardingRouter : OnboardingRouter \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt new file mode 100644 index 0000000000..5704da9b7f --- /dev/null +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/navigation/OnboardingRouter.kt @@ -0,0 +1,12 @@ +package com.tangem.feature.onboarding.navigation + +/** + * Onboarding router + */ +// TODO: Move to onboarding api module [REDACTED_JIRA] +interface OnboardingRouter { + + companion object { + const val CAN_SKIP_BACKUP = "onboarding_wallet_can_skip_backup" + } +} \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index b48335e242..d95c4421f7 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -66,6 +66,9 @@ dependencies { implementation(projects.domain.appCurrency.models) implementation(projects.domain.balanceHiding) + //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] + implementation(projects.features.onboarding) + /** Feature Apis */ implementation(projects.features.wallet.api) implementation(projects.features.tokendetails.api) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 6ad00bcf52..198df54ab0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -21,6 +21,7 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.onboarding.navigation.OnboardingRouter import com.tangem.feature.wallet.presentation.WalletFragment import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel @@ -100,7 +101,12 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr } override fun openOnboardingScreen() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) + reduxNavController.navigate( + action = NavigationAction.NavigateTo( + screen = AppScreen.OnboardingWallet, + bundle = bundleOf(OnboardingRouter.CAN_SKIP_BACKUP to false), + ), + ) } override fun openTxHistoryWebsite(url: String) { From 04195a9f123c538ad4e788e94041a5d655569576 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 18:54:39 +0300 Subject: [PATCH 11/12] Updated on 2026-08-14 --- .../feature/wallet/presentation/WalletFragment.kt | 2 +- .../wallet/presentation/router/DefaultWalletRouter.kt | 11 +++++------ .../wallet/presentation/router/InnerWalletRouter.kt | 5 ++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index 91a98afe61..e8c0881cfe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -38,7 +38,7 @@ internal class WalletFragment : ComposeFragment() { setSystemBarsColor(systemBarsColor) } - _walletRouter.Initialize(fragmentManager = requireActivity().supportFragmentManager) + _walletRouter.Initialize(onFinish = requireActivity()::finish) } companion object { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 198df54ab0..08206005d7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -7,7 +7,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.core.os.bundleOf import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController @@ -34,13 +33,13 @@ import kotlin.properties.Delegates internal class DefaultWalletRouter(private val reduxNavController: ReduxNavController) : InnerWalletRouter { private var navController: NavHostController by Delegates.notNull() - private var fragmentManager: FragmentManager by Delegates.notNull() + private var onFinish: () -> Unit = {} override fun getEntryFragment(): Fragment = WalletFragment.create() @Composable - override fun Initialize(fragmentManager: FragmentManager) { - this.fragmentManager = fragmentManager + override fun Initialize(onFinish: () -> Unit) { + this.onFinish = onFinish NavHost( navController = rememberNavController().apply { navController = this }, @@ -80,10 +79,10 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr * 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) { - if (screen != null) { + if (screen == AppScreen.Home) { reduxNavController.navigate(action = NavigationAction.PopBackTo(screen)) } else { - fragmentManager.popBackStack() + onFinish.invoke() } } else { navController.popBackStack() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 07a2e8a80b..8234b7a917 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.fragment.app.FragmentManager import com.tangem.core.navigation.AppScreen import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId @@ -22,11 +21,11 @@ internal interface InnerWalletRouter : WalletRouter { /** * Initialize router * - * @param fragmentManager fragment manager + * @param onFinish finish activity callback */ @Suppress("TopLevelComposableFunctions") @Composable - fun Initialize(fragmentManager: FragmentManager) + fun Initialize(onFinish: () -> Unit) /** Pop back stack */ fun popBackStack(screen: AppScreen? = null) From fc0da8e978e8d1beb10a40d426958327bc7694f7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 9 Oct 2023 15:48:05 +0300 Subject: [PATCH 12/12] Updated on 2026-08-14 --- .../viewmodels/AddCustomTokenViewModel.kt | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 660be89b17..16d07a40ae 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -22,7 +22,10 @@ import com.tangem.domain.common.extensions.* import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.features.addCustomToken.CustomCurrency +import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken @@ -59,14 +62,16 @@ import javax.inject.Inject * [REDACTED_AUTHOR] */ -@Suppress("LargeClass") +@Suppress("LargeClass", "LongParameterList") @HiltViewModel internal class AddCustomTokenViewModel @Inject constructor( analyticsEventHandler: AnalyticsEventHandler, featureRouter: CustomTokenRouter, + getCurrenciesUseCase: GetCryptoCurrenciesUseCase, private val featureInteractor: CustomTokenInteractor, private val dispatchers: AppCoroutineDispatcherProvider, private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val walletFeatureToggles: WalletFeatureToggles, ) : ViewModel(), DefaultLifecycleObserver { private val analyticsSender = AddCustomTokenAnalyticsSender(analyticsEventHandler) @@ -74,12 +79,30 @@ internal class AddCustomTokenViewModel @Inject constructor( private val testActionsHandler = TestActionsHandler() private val formStateBuilder = FormStateBuilder() + private var currentCryptoCurrencies: List = emptyList() + /** Screen state */ var uiState by mutableStateOf(getInitialUiState()) private set private var foundToken: FoundToken? = null + init { + if (walletFeatureToggles.isRedesignedScreenEnabled) { + viewModelScope.launch(dispatchers.main) { + currentCryptoCurrencies = getSelectedWalletUseCase().fold( + ifLeft = { emptyList() }, + ifRight = { selectedWallet -> + getCurrenciesUseCase(selectedWallet.walletId).fold( + ifLeft = { emptyList() }, + ifRight = { it }, + ) + }, + ) + } + } + } + override fun onCreate(owner: LifecycleOwner) { analyticsSender.sendWhenScreenOpened() } @@ -564,6 +587,33 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isTokenAlreadyAdded(): Boolean { + return if (walletFeatureToggles.isRedesignedScreenEnabled) { + isTokenAlreadyAddedNew() + } else { + isTokenAlreadyAddedOld() + } + } + + private fun isTokenAlreadyAddedNew(): Boolean { + return currentCryptoCurrencies + .filterIsInstance() + .any { token -> + val contractAddress = uiState.form.contractAddressInputField.value + val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain + val networkId = Blockchain.fromNetworkId(networkSelectorValue.toNetworkId())?.id + + val savedTokenId = if (token.isCustom) null else token.id.value + + val sameId = foundToken?.id == savedTokenId + val sameAddress = contractAddress == token.contractAddress + val sameBlockchain = networkId == token.network.id.value + val isSameDerivationPath = getDerivationPath()?.rawPath == token.network.derivationPath.value + + sameId && sameAddress && sameBlockchain && isSameDerivationPath + } + } + + private fun isTokenAlreadyAddedOld(): Boolean { return store.state.walletState.walletsStores .map { walletStore -> walletStore.walletsData.map(WalletDataModel::currency) } .flatten() @@ -581,6 +631,23 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isBlockchainAlreadyAdded(): Boolean { + return if (walletFeatureToggles.isRedesignedScreenEnabled) { + isBlockchainAlreadyAddedNew() + } else { + isBlockchainAlreadyAddedOld() + } + } + + private fun isBlockchainAlreadyAddedNew(): Boolean { + return currentCryptoCurrencies + .filterIsInstance() + .any { coin -> + coin.network.id.value == uiState.form.networkSelectorField.selectedItem.blockchain.id && + coin.network.derivationPath.value == getDerivationPath()?.rawPath + } + } + + private fun isBlockchainAlreadyAddedOld(): Boolean { return store.state.walletState.walletsStores .map { walletStore -> walletStore.walletsData.map(WalletDataModel::currency) } .flatten()