From 2b4aa16605c1aedfdf0ee8d8d7d85a9de5a545f5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 16 Nov 2023 13:15:59 +0300 Subject: [PATCH 1/6] Updated on 2026-08-14 --- .../domain/SendRecipientListContent.kt | 1 + .../presentation/state/SendStateFactory.kt | 7 +- .../recipient/SendRecipientListConverter.kt | 71 +++++++++++-------- .../ui/recipient/SendRecipientContent.kt | 41 +++++++---- .../presentation/viewmodel/SendViewModel.kt | 20 +++++- 5 files changed, 93 insertions(+), 47 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt index 6cb3516497..7f7216e6e3 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/domain/SendRecipientListContent.kt @@ -17,5 +17,6 @@ internal sealed class SendRecipientListContent { data class Wallets( val list: PersistentList, + val isWalletsOnly: Boolean, ) : SendRecipientListContent() } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index 3bb47678a9..38583d0676 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -105,10 +105,15 @@ internal class SendStateFactory( //endregion //region recipient - fun onLoadedRecipientList(wallets: List, txHistory: PagingData) { + fun onLoadedRecipientList( + wallets: List, + txHistory: PagingData, + txHistoryCount: Int, + ) { recipientListStateConverter.convert( wallets = wallets, txHistory = txHistory, + txHistoryCount = txHistoryCount, ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt index da8a97622d..8fd734e781 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientListConverter.kt @@ -22,7 +22,7 @@ internal class SendRecipientListConverter( private val cryptoCurrencyStatusProvider: Provider, ) { - fun convert(wallets: List, txHistory: PagingData) { + fun convert(wallets: List, txHistory: PagingData, txHistoryCount: Int) { val filteredWallets = wallets.filterNotNull() .groupBy { item -> item.name } .values.flatten() @@ -32,49 +32,58 @@ internal class SendRecipientListConverter( ) } + val walletsItem = getWalletItems(filteredWallets, txHistoryCount) + currentStateProvider().recipientList.update { - txHistory.filter { item -> - val isTransfer = item.type == TxHistoryItem.TransactionType.Transfer - val isNotContract = item.interactionAddressType is TxHistoryItem.InteractionAddressType.User - val isSingleAddress = if (item.isOutgoing) { - item.destinationType is TxHistoryItem.DestinationType.Single - } else { - item.sourceType is TxHistoryItem.SourceType.Single - } - isTransfer && isSingleAddress && isNotContract - }.map { tx -> - SendRecipientListContent.Item( - id = tx.txHash, - title = tx.extractAddress(), - subtitle = TextReference.Str(tx.getAmount()), - info = tx.extractTimestamp(), - subtitleIconRes = tx.extractIconRes(), - ) - }.insertWallets(filteredWallets) + if (txHistoryCount == 0) { + PagingData.from(listOf(walletsItem)) + } else { + txHistory.filter { item -> + val isTransfer = item.type == TxHistoryItem.TransactionType.Transfer + val isNotContract = item.interactionAddressType is TxHistoryItem.InteractionAddressType.User + val isSingleAddress = if (item.isOutgoing) { + item.destinationType is TxHistoryItem.DestinationType.Single + } else { + item.sourceType is TxHistoryItem.SourceType.Single + } + isTransfer && isSingleAddress && isNotContract + }.map { tx -> + SendRecipientListContent.Item( + id = tx.txHash, + title = tx.extractAddress(), + subtitle = TextReference.Str(tx.getAmount()), + info = tx.extractTimestamp(), + subtitleIconRes = tx.extractIconRes(), + ) + }.insertWallets(walletsItem) + } } } private fun PagingData.insertWallets( - wallets: List, + wallets: SendRecipientListContent.Wallets, ): PagingData { return insertSeparators(terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE) { before, after -> return@insertSeparators when { - before == null && after is SendRecipientListContent.Item -> { - SendRecipientListContent.Wallets( - wallets.map { - SendRecipientListContent.Item( - id = it.address, - title = TextReference.Str(it.address), - subtitle = TextReference.Str(it.name), - ) - }.toPersistentList(), - ) - } + before == null && after is SendRecipientListContent.Item -> wallets else -> null } } } + private fun getWalletItems(wallets: List, txHistoryCount: Int): SendRecipientListContent.Wallets { + return SendRecipientListContent.Wallets( + wallets.map { + SendRecipientListContent.Item( + id = it.address, + title = TextReference.Str(it.address), + subtitle = TextReference.Str(it.name), + ) + }.toPersistentList(), + isWalletsOnly = txHistoryCount == 0, + ) + } + private fun TxHistoryItem.extractAddress(): TextReference = if (isOutgoing) { when (val destination = destinationType) { is TxHistoryItem.DestinationType.Multiple -> TextReference.Res( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt index ada2667527..30563155b4 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/SendRecipientContent.kt @@ -80,6 +80,7 @@ internal fun SendRecipientContent( } } +@Suppress("LongMethod") @OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.recipientListItem( recipientList: LazyPagingItems, @@ -106,10 +107,17 @@ private fun LazyListScope.recipientListItem( .padding(top = TangemTheme.dimens.spacing20) .then( if (index == 0) { + val bottomRadius = if (item.isWalletsOnly) { + TangemTheme.dimens.radius12 + } else { + TangemTheme.dimens.radius0 + } Modifier.clip( RoundedCornerShape( topEnd = TangemTheme.dimens.radius12, topStart = TangemTheme.dimens.radius12, + bottomStart = bottomRadius, + bottomEnd = bottomRadius, ), ) } else { @@ -182,18 +190,25 @@ private fun RecipientWalletListItem( onClick = { clickIntents.onRecipientAddressValueChange(title) }, ) } - Text( - text = stringResource(R.string.send_recent_transactions), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - modifier = Modifier - .fillMaxWidth() - .padding( - top = TangemTheme.dimens.spacing8, - bottom = TangemTheme.dimens.spacing8, - start = TangemTheme.dimens.spacing12, - end = TangemTheme.dimens.spacing12, - ), - ) + if (!item.isWalletsOnly) { + val topPadding = if (item.list.isNotEmpty()) { + TangemTheme.dimens.spacing8 + } else { + TangemTheme.dimens.spacing0 + } + Text( + text = stringResource(R.string.send_recent_transactions), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier + .fillMaxWidth() + .padding( + top = topPadding, + bottom = TangemTheme.dimens.spacing8, + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing12, + ), + ) + } } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 3e8bc7477f..02f2caefc6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -20,6 +20,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.txhistory.models.TxHistoryItem +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 @@ -57,6 +58,7 @@ internal class SendViewModel @Inject constructor( private val getWalletsUseCase: GetWalletsUseCase, private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, + private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val getFeeUseCase: GetFeeUseCase, private val walletManagersFacade: WalletManagersFacade, savedStateHandle: SavedStateHandle, @@ -97,7 +99,6 @@ internal class SendViewModel @Inject constructor( override fun onCreate(owner: LifecycleOwner) { getWalletAddresses() subscribeOnCurrencyStatusUpdates(owner) - getWalletsAndRecent() getFee() } @@ -134,6 +135,7 @@ internal class SendViewModel @Inject constructor( .onEach { either -> either.onRight { cryptoCurrencyStatus = it + getWalletsAndRecent() uiState = stateFactory.getReadyState() } } @@ -158,10 +160,12 @@ internal class SendViewModel @Inject constructor( combine( flow = getUserWallets().conflate(), flow2 = getTxHistory().conflate(), - ) { wallets, txHistory -> + flow3 = getTxHistoryCount().conflate(), + ) { wallets, txHistory, txHistoryCount -> stateFactory.onLoadedRecipientList( wallets = wallets, txHistory = txHistory, + txHistoryCount = txHistoryCount, ) } .flowOn(dispatchers.io) @@ -213,6 +217,18 @@ internal class SendViewModel @Inject constructor( } } + private fun getTxHistoryCount(): Flow { + return flow { + txHistoryItemsCountUseCase( + userWalletId = userWalletId, + currency = cryptoCurrency, + ).fold( + ifRight = { emit(it) }, + ifLeft = { emit(0) }, + ) + } + } + private fun getFee() { viewModelScope.launch(dispatchers.main) { uiState.currentState From 7fdf9167eab5bbc0736ce5e02f38edfc8693a7e7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 Nov 2023 21:21:39 +0800 Subject: [PATCH 2/6] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 6 -- .../datasource/di/AppCurrencyDataModule.kt | 26 +---- .../datasource/di/AppThemeModeDataModule.kt | 29 ------ .../tangem/datasource/di/CardDataModule.kt | 34 ------- .../datasource/di/HiddenBalanceDataModule.kt | 32 ------ .../datasource/di/SettingsDataModule.kt | 50 ---------- .../tangem/datasource/di/WalletsDataModule.kt | 26 ----- .../datasource/local/AppPreferenceStorage.kt | 1 + .../appcurrency/BalanceHidingSettingsStore.kt | 13 --- .../appcurrency/SelectedAppCurrencyStore.kt | 15 --- .../BalanceStateHidingSettingsStore.kt | 19 ---- .../DefaultSelectedAppCurrencyStore.kt | 10 -- .../local/apptheme/AppThemeModeStore.kt | 13 --- .../apptheme/DefaultAppThemeModeStore.kt | 9 -- .../datasource/local/card/UsedCardsStore.kt | 21 ---- .../BooleanSharedPreferencesDataStore.kt | 16 --- .../IntSharedPreferencesDataStore.kt | 16 --- .../JsonSharedPreferencesDataStore.kt | 24 ----- .../LongSharedPreferencesDataStore.kt | 16 --- .../datastore/SharedPreferencesDataStore.kt | 98 ------------------- .../local/preferences/PreferencesDataStore.kt | 34 ++++++- .../local/preferences/PreferencesKeys.kt | 40 +++++++- .../local/settings/AppLaunchCountStore.kt | 21 ---- .../settings/AppRatingShowingCountStore.kt | 21 ---- .../settings/FundsFoundDateInMillisStore.kt | 21 ---- .../settings/UserInteractingStatusStore.kt | 21 ---- .../userwallet/ShouldSaveUserWalletStore.kt | 24 ----- data/app-currency/build.gradle.kts | 5 +- .../DefaultAppCurrencyRepository.kt | 31 +++--- .../appcurrency/di/AppCurrencyDataModule.kt | 14 +-- data/app-theme/build.gradle.kts | 3 + .../apptheme/DefaultAppThemeModeRepository.kt | 32 +++--- .../apptheme/di/AppThemeModeDataModule.kt | 10 +- data/balance-hiding/build.gradle.kts | 2 +- .../DefaultBalanceHidingRepository.kt | 43 ++++---- .../balancehiding/di/BalanceHidingModule.kt | 15 +-- data/card/build.gradle.kts | 2 + .../tangem/data/card/DefaultCardRepository.kt | 44 ++++----- .../com/tangem/data/card/di/CardDataModule.kt | 10 +- data/settings/build.gradle.kts | 19 ++-- .../settings/DefaultAppRatingRepository.kt | 84 ++++++---------- .../data/settings/di/SettingsDataModule.kt | 21 +--- data/tokens/build.gradle.kts | 3 + .../tangem/data/tokens/di/TokensDataModule.kt | 6 +- .../repository/DefaultQuotesRepository.kt | 23 +++-- data/wallets/build.gradle.kts | 4 +- .../data/wallets/DefaultWalletsRepository.kt | 25 +++-- .../data/wallets/di/WalletsDataModule.kt | 15 +-- .../repositories/AppRatingRepository.kt | 3 - .../wallets/repository/WalletsRepository.kt | 2 - 50 files changed, 256 insertions(+), 816 deletions(-) delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/AppThemeModeDataModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/CardDataModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/HiddenBalanceDataModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/SettingsDataModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/WalletsDataModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/BalanceHidingSettingsStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/BalanceStateHidingSettingsStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/apptheme/AppThemeModeStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/apptheme/DefaultAppThemeModeStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/card/UsedCardsStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/BooleanSharedPreferencesDataStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/IntSharedPreferencesDataStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/JsonSharedPreferencesDataStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/LongSharedPreferencesDataStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/settings/AppLaunchCountStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/settings/AppRatingShowingCountStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/settings/FundsFoundDateInMillisStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/settings/UserInteractingStatusStore.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/userwallet/ShouldSaveUserWalletStore.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index c67f289238..420a6ff091 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -30,7 +30,6 @@ import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.common.LogConfig -import com.tangem.domain.settings.repositories.AppRatingRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -209,9 +208,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var userTokensStore: UserTokensStore - @Inject - lateinit var appRatingRepository: AppRatingRepository - @Inject lateinit var getAppThemeModeUseCase: GetAppThemeModeUseCase @@ -248,10 +244,8 @@ internal class TapApplication : Application(), ImageLoaderFactory { // TODO: Try to performance and user experience. // [REDACTED_JIRA] runBlocking { - walletsRepository.initialize() initUserWalletsListManager() featureTogglesManager.init() - appRatingRepository.initialize() // learn2earnInteractor.init() } diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt index e25e15471b..fade673123 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt @@ -1,18 +1,11 @@ package com.tangem.datasource.di -import android.content.Context -import com.squareup.moshi.Moshi -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore import com.tangem.datasource.local.appcurrency.implementation.DefaultAvailableAppCurrenciesStore -import com.tangem.datasource.local.appcurrency.implementation.DefaultSelectedAppCurrencyStore -import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore import com.tangem.datasource.local.datastore.RuntimeDataStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @@ -23,23 +16,6 @@ internal object AppCurrencyDataModule { @Provides @Singleton fun provideAvailableAppCurrenciesStore(): AvailableAppCurrenciesStore { - return DefaultAvailableAppCurrenciesStore( - dataStore = RuntimeDataStore(), - ) - } - - @Provides - @Singleton - fun provideSelectedAppCurrencyStore( - @ApplicationContext context: Context, - @NetworkMoshi moshi: Moshi, - ): SelectedAppCurrencyStore { - return DefaultSelectedAppCurrencyStore( - dataStore = JsonSharedPreferencesDataStore( - preferencesName = "selected_app_currency", - context = context, - adapter = moshi.adapter(CurrenciesResponse.Currency::class.java), - ), - ) + return DefaultAvailableAppCurrenciesStore(dataStore = RuntimeDataStore()) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppThemeModeDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppThemeModeDataModule.kt deleted file mode 100644 index a6e8020146..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppThemeModeDataModule.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.datasource.di - -import android.content.Context -import com.squareup.moshi.Moshi -import com.tangem.datasource.local.apptheme.AppThemeModeStore -import com.tangem.datasource.local.apptheme.DefaultAppThemeModeStore -import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore -import com.tangem.domain.apptheme.model.AppThemeMode -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal object AppThemeModeDataModule { - - @Provides - fun provideAppThemeModeStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): AppThemeModeStore { - return DefaultAppThemeModeStore( - dataStore = JsonSharedPreferencesDataStore( - preferencesName = "app_theme", - context = context, - adapter = moshi.adapter(AppThemeMode::class.java), - ), - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/CardDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/CardDataModule.kt deleted file mode 100644 index f06e5ac8e5..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/CardDataModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.datasource.di - -import android.content.Context -import com.squareup.moshi.Moshi -import com.squareup.moshi.Types -import com.tangem.datasource.local.card.DefaultUsedCardsStore -import com.tangem.datasource.local.card.UsedCardInfo -import com.tangem.datasource.local.card.UsedCardsStore -import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object CardDataModule { - - @Provides - @Singleton - fun provideUsedCardsStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): UsedCardsStore { - return DefaultUsedCardsStore( - store = JsonSharedPreferencesDataStore( - preferencesName = "tapPrefs", - context = context, - adapter = moshi.adapter( - Types.newParameterizedType(List::class.java, UsedCardInfo::class.java), - ), - ), - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/HiddenBalanceDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/HiddenBalanceDataModule.kt deleted file mode 100644 index 9b59f30477..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/HiddenBalanceDataModule.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.datasource.di - -import android.content.Context -import com.squareup.moshi.Moshi -import com.tangem.datasource.local.appcurrency.BalanceHidingSettingsStore -import com.tangem.datasource.local.appcurrency.implementation.BalanceStateHidingSettingsStore -import com.tangem.datasource.local.datastore.JsonSharedPreferencesDataStore -import com.tangem.domain.balancehiding.BalanceHidingSettings -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal object HiddenBalanceDataModule { - - @Provides - fun provideHiddenBalanceStateStore( - @ApplicationContext context: Context, - @NetworkMoshi moshi: Moshi, - ): BalanceHidingSettingsStore { - return BalanceStateHidingSettingsStore( - dataStore = JsonSharedPreferencesDataStore( - preferencesName = "balance_hiding_settings", - context = context, - adapter = moshi.adapter(BalanceHidingSettings::class.java), - ), - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/SettingsDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/SettingsDataModule.kt deleted file mode 100644 index ba757806d6..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/SettingsDataModule.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.tangem.datasource.di - -import android.content.Context -import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.LongSharedPreferencesDataStore -import com.tangem.datasource.local.settings.* -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object SettingsDataModule { - - @Provides - @Singleton - fun provideAppLaunchCountStore(@ApplicationContext context: Context): AppLaunchCountStore { - return DefaultAppLaunchCountStore( - store = IntSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context), - ) - } - - @Provides - @Singleton - fun provideAppRatingShowingCountStore(@ApplicationContext context: Context): AppRatingShowingCountStore { - return DefaultAppRatingShowingCountStore( - store = IntSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context), - ) - } - - @Provides - @Singleton - fun provideFundsFoundDateInMillisStore(@ApplicationContext context: Context): FundsFoundDateInMillisStore { - return DefaultFundsFoundDateInMillisStore( - store = LongSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context), - ) - } - - @Provides - @Singleton - fun provideUserInteractingStatusStore(@ApplicationContext context: Context): UserInteractingStatusStore { - return DefaultUserInteractingStatusStore( - store = BooleanSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context), - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/WalletsDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/WalletsDataModule.kt deleted file mode 100644 index 61cdcacad3..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/WalletsDataModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.datasource.di - -import android.content.Context -import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore -import com.tangem.datasource.local.settings.* -import com.tangem.datasource.local.userwallet.DefaultShouldSaveUserWalletStore -import com.tangem.datasource.local.userwallet.ShouldSaveUserWalletStore -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object WalletsDataModule { - - @Provides - @Singleton - fun provideShouldSaveUserWalletsStore(@ApplicationContext context: Context): ShouldSaveUserWalletStore { - return DefaultShouldSaveUserWalletStore( - store = BooleanSharedPreferencesDataStore(preferencesName = "tapPrefs", context = context), - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt b/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt index 3e30c7344b..d56ca29fa0 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt @@ -5,6 +5,7 @@ package com.tangem.datasource.local * [REDACTED_AUTHOR] */ +@Deprecated(message = "Use AppPreferencesStore", level = DeprecationLevel.WARNING) interface AppPreferenceStorage { /** Json config with feature toggles 'ToggleName: String - Availability: Boolean' */ diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/BalanceHidingSettingsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/BalanceHidingSettingsStore.kt deleted file mode 100644 index a29714251b..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/BalanceHidingSettingsStore.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.datasource.local.appcurrency - -import com.tangem.domain.balancehiding.BalanceHidingSettings -import kotlinx.coroutines.flow.Flow - -interface BalanceHidingSettingsStore { - - fun get(): Flow - - suspend fun getSyncOrDefault(): BalanceHidingSettings - - suspend fun store(settings: BalanceHidingSettings) -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt deleted file mode 100644 index e732ce8415..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.datasource.local.appcurrency - -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse -import kotlinx.coroutines.flow.Flow - -interface SelectedAppCurrencyStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): CurrenciesResponse.Currency? - - suspend fun store(item: CurrenciesResponse.Currency) - - suspend fun isEmpty(): Boolean -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/BalanceStateHidingSettingsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/BalanceStateHidingSettingsStore.kt deleted file mode 100644 index 0314d6549d..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/BalanceStateHidingSettingsStore.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.datasource.local.appcurrency.implementation - -import com.tangem.datasource.local.appcurrency.BalanceHidingSettingsStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.domain.balancehiding.BalanceHidingSettings - -internal class BalanceStateHidingSettingsStore( - dataStore: StringKeyDataStore, -) : BalanceHidingSettingsStore, KeylessDataStoreDecorator(dataStore) { - - override suspend fun getSyncOrDefault(): BalanceHidingSettings { - return getSyncOrNull() ?: BalanceHidingSettings( - isHidingEnabledInSettings = false, - isBalanceHidden = false, - isBalanceHidingNotificationEnabled = true, - ) - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt deleted file mode 100644 index c79bf3045b..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.datasource.local.appcurrency.implementation - -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import com.tangem.datasource.local.datastore.core.StringKeyDataStore - -internal class DefaultSelectedAppCurrencyStore( - dataStore: StringKeyDataStore, -) : SelectedAppCurrencyStore, KeylessDataStoreDecorator(dataStore) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/AppThemeModeStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/AppThemeModeStore.kt deleted file mode 100644 index 78e1fd514c..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/AppThemeModeStore.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.datasource.local.apptheme - -import com.tangem.domain.apptheme.model.AppThemeMode -import kotlinx.coroutines.flow.Flow - -interface AppThemeModeStore { - - fun get(): Flow - - suspend fun store(item: AppThemeMode) - - suspend fun isEmpty(): Boolean -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/DefaultAppThemeModeStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/DefaultAppThemeModeStore.kt deleted file mode 100644 index 483bf4b525..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/apptheme/DefaultAppThemeModeStore.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.datasource.local.apptheme - -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.domain.apptheme.model.AppThemeMode - -internal class DefaultAppThemeModeStore( - dataStore: StringKeyDataStore, -) : AppThemeModeStore, KeylessDataStoreDecorator(dataStore) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/card/UsedCardsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/card/UsedCardsStore.kt deleted file mode 100644 index 0d54995dbb..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/card/UsedCardsStore.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.datasource.local.card - -import com.tangem.datasource.local.datastore.SharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -interface UsedCardsStore { - - fun get(): Flow> - - suspend fun getSyncOrNull(): List? - - suspend fun store(item: List) -} - -internal class DefaultUsedCardsStore( - store: SharedPreferencesDataStore>, -) : UsedCardsStore, KeylessDataStoreDecorator>( - wrappedDataStore = store, - key = "usedCardsInfo_v2", -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/BooleanSharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/BooleanSharedPreferencesDataStore.kt deleted file mode 100644 index 36141207c0..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/BooleanSharedPreferencesDataStore.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.datasource.local.datastore - -import android.content.Context -import androidx.core.content.edit - -internal class BooleanSharedPreferencesDataStore( - preferencesName: String, - context: Context, -) : SharedPreferencesDataStore(preferencesName, context) { - - override fun getByKey(key: String): Boolean = sharedPreferences.getBoolean(key, false) - - override fun storeByKey(key: String, value: Boolean) { - sharedPreferences.edit { putBoolean(key, value) } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/IntSharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/IntSharedPreferencesDataStore.kt deleted file mode 100644 index 7ef0f70fa6..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/IntSharedPreferencesDataStore.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.datasource.local.datastore - -import android.content.Context -import androidx.core.content.edit - -internal class IntSharedPreferencesDataStore( - preferencesName: String, - context: Context, -) : SharedPreferencesDataStore(preferencesName, context) { - - override fun getByKey(key: String): Int = sharedPreferences.getInt(key, 0) - - override fun storeByKey(key: String, value: Int) { - sharedPreferences.edit { putInt(key, value) } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/JsonSharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/JsonSharedPreferencesDataStore.kt deleted file mode 100644 index 5e47ee8114..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/JsonSharedPreferencesDataStore.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.datasource.local.datastore - -import android.content.Context -import androidx.core.content.edit -import com.squareup.moshi.JsonAdapter - -internal class JsonSharedPreferencesDataStore( - preferencesName: String, - context: Context, - private val adapter: JsonAdapter, -) : SharedPreferencesDataStore(preferencesName, context) { - - override fun getByKey(key: String): Value? { - val json = sharedPreferences.getString(key, null) ?: return null - - return adapter.fromJson(json) - } - - override fun storeByKey(key: String, value: Value) { - val json = adapter.toJson(value) - - sharedPreferences.edit { putString(key, json) } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/LongSharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/LongSharedPreferencesDataStore.kt deleted file mode 100644 index b1bb00e4e1..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/LongSharedPreferencesDataStore.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.datasource.local.datastore - -import android.content.Context -import androidx.core.content.edit - -internal class LongSharedPreferencesDataStore( - preferencesName: String, - context: Context, -) : SharedPreferencesDataStore(preferencesName, context) { - - override fun getByKey(key: String): Long = sharedPreferences.getLong(key, 0) - - override fun storeByKey(key: String, value: Long) { - sharedPreferences.edit { putLong(key, value) } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt deleted file mode 100644 index 417b4e9ac4..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.tangem.datasource.local.datastore - -import android.content.Context -import android.content.Context.MODE_PRIVATE -import android.content.SharedPreferences -import androidx.core.content.edit -import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.datasource.local.datastore.utils.Trigger -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.mapNotNull -import timber.log.Timber - -internal abstract class SharedPreferencesDataStore( - preferencesName: String, - context: Context, -) : StringKeyDataStore { - - protected val sharedPreferences: SharedPreferences by lazy { - context.getSharedPreferences(preferencesName, MODE_PRIVATE) - } - - private val writeTrigger = Trigger() - - abstract fun getByKey(key: String): Value? - - abstract fun storeByKey(key: String, value: Value) - - override suspend fun isEmpty(): Boolean { - return sharedPreferences.all.isEmpty() - } - - override suspend fun contains(key: String): Boolean { - return sharedPreferences.contains(key) - } - - override fun get(key: String): Flow { - return writeTrigger - .mapNotNull { getInternal(key) } - .distinctUntilChanged() - } - - override fun getAll(): Flow> { - throw UnsupportedOperationException("Unknown key") - } - - override suspend fun getSyncOrNull(key: String): Value? = getInternal(key) - - override suspend fun getAllSyncOrNull(): List { - throw UnsupportedOperationException("Unknown key") - } - - override suspend fun store(key: String, value: Value) { - try { - storeByKey(key, value) - writeTrigger.trigger() - } catch (e: Throwable) { - Timber.e(e, "Unable to edit preferences: $key") - } - } - - override suspend fun store(values: Map) { - values.forEach { (key, item) -> - store(key, item) - } - } - - override suspend fun remove(key: String) { - sharedPreferences.edit { remove(key) } - writeTrigger.trigger() - } - - override suspend fun remove(keys: Collection) { - sharedPreferences.edit { - keys.forEach { key -> - remove(key) - } - } - - writeTrigger.trigger() - } - - override suspend fun clear() { - sharedPreferences.edit { clear() } - writeTrigger.trigger() - } - - private fun getInternal(key: String): Value? { - if (!sharedPreferences.contains(key)) return null - - return try { - getByKey(key) - } catch (e: Throwable) { - Timber.e(e, "Unable to get value from preferences: $key") - null - } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt index 7d0d6ba94a..9e9b278b7e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesDataStore.kt @@ -4,11 +4,13 @@ import android.content.Context import androidx.datastore.core.DataMigration import androidx.datastore.core.DataStore import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler +import androidx.datastore.preferences.SharedPreferencesMigration import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.preferencesDataStoreFile import com.tangem.datasource.local.preferences.PreferencesDataStore.INSTANCE +import com.tangem.datasource.local.preferences.utils.SharedPreferencesKeyMigration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import timber.log.Timber @@ -23,6 +25,8 @@ import kotlin.coroutines.CoroutineContext internal object PreferencesDataStore { private const val PREFERENCES_FILE_NAME = "TAP_PREFS" + private const val LEGACY_TAP_PREFS_FILE_NAME = "tapPrefs" + private const val LEGACY_DEFAULT_KEY_NAME = "key" private var INSTANCE: DataStore? = null @@ -33,7 +37,7 @@ internal object PreferencesDataStore { private fun create(context: Context, dispatcher: CoroutineContext): DataStore { return PreferenceDataStoreFactory.create( corruptionHandler = createCorruptionHandler(), - migrations = createMigrations(), + migrations = createMigrations(context = context), scope = CoroutineScope(context = dispatcher + SupervisorJob()), produceFile = { context.preferencesDataStoreFile(name = PREFERENCES_FILE_NAME) }, ) @@ -48,7 +52,31 @@ internal object PreferencesDataStore { ) } - private fun createMigrations(): List> { - return listOf() + private fun createMigrations(context: Context): List> { + return listOf( + SharedPreferencesMigration( + context = context, + sharedPreferencesName = LEGACY_TAP_PREFS_FILE_NAME, + keysToMigrate = getTapPrefKeysToMigrate(), + ), + SharedPreferencesKeyMigration( + context = context, + legacyPrefsName = "app_theme", + legacyKeyName = LEGACY_DEFAULT_KEY_NAME, + keyName = PreferencesKeys.APP_THEME_MODE_KEY.name, + ), + SharedPreferencesKeyMigration( + context = context, + legacyPrefsName = "selected_app_currency", + legacyKeyName = LEGACY_DEFAULT_KEY_NAME, + keyName = PreferencesKeys.SELECTED_APP_CURRENCY_KEY.name, + ), + SharedPreferencesKeyMigration( + context = context, + legacyPrefsName = "balance_hiding_settings", + legacyKeyName = LEGACY_DEFAULT_KEY_NAME, + keyName = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY.name, + ), + ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 90f2b61270..d81065c147 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -1,13 +1,49 @@ package com.tangem.datasource.local.preferences +import androidx.datastore.preferences.core.* +import com.tangem.datasource.local.preferences.PreferencesKeys.APP_LAUNCH_COUNT_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.FUNDS_FOUND_DATE_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.SAVE_USER_WALLETS_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.USED_CARDS_INFO_KEY +import com.tangem.datasource.local.preferences.PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY + /** * All preferences keys that DataStore is stored. * [REDACTED_AUTHOR] */ -object PreferencesKeys +object PreferencesKeys { + + val SAVE_USER_WALLETS_KEY by lazy { booleanPreferencesKey(name = "saveUserWallets") } + + val APP_LAUNCH_COUNT_KEY by lazy { intPreferencesKey(name = "launchCount") } + + val SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY by lazy { intPreferencesKey(name = "showRatingDialogAtLaunchCount") } + + val FUNDS_FOUND_DATE_KEY by lazy { longPreferencesKey(name = "fundsFoundDate") } + + val USER_WAS_INTERACT_WITH_RATING_KEY by lazy { booleanPreferencesKey(name = "userWasInteractWithRating") } + + val USED_CARDS_INFO_KEY by lazy { stringPreferencesKey(name = "usedCardsInfo_v2") } + + val APP_THEME_MODE_KEY by lazy { stringPreferencesKey(name = "appThemeMode") } + + val SELECTED_APP_CURRENCY_KEY by lazy { stringPreferencesKey(name = "selectedAppCurrency") } + + val BALANCE_HIDING_SETTINGS_KEY by lazy { stringPreferencesKey(name = "balanceHidingSettings") } +} /** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore */ internal fun getTapPrefKeysToMigrate(): Set { - return setOf() + return setOf( + SAVE_USER_WALLETS_KEY, + APP_LAUNCH_COUNT_KEY, + SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY, + FUNDS_FOUND_DATE_KEY, + USER_WAS_INTERACT_WITH_RATING_KEY, + USED_CARDS_INFO_KEY, + ) + .map(Preferences.Key<*>::name) + .toSet() } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppLaunchCountStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppLaunchCountStore.kt deleted file mode 100644 index a2ee494307..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppLaunchCountStore.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.datasource.local.settings - -import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -interface AppLaunchCountStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): Int? - - suspend fun store(item: Int) -} - -internal class DefaultAppLaunchCountStore( - store: IntSharedPreferencesDataStore, -) : AppLaunchCountStore, KeylessDataStoreDecorator( - wrappedDataStore = store, - key = "launchCount", -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppRatingShowingCountStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppRatingShowingCountStore.kt deleted file mode 100644 index dfbb452f22..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/settings/AppRatingShowingCountStore.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.datasource.local.settings - -import com.tangem.datasource.local.datastore.IntSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -interface AppRatingShowingCountStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): Int? - - suspend fun store(item: Int) -} - -internal class DefaultAppRatingShowingCountStore( - store: IntSharedPreferencesDataStore, -) : AppRatingShowingCountStore, KeylessDataStoreDecorator( - wrappedDataStore = store, - key = "showRatingDialogAtLaunchCount", -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/settings/FundsFoundDateInMillisStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/settings/FundsFoundDateInMillisStore.kt deleted file mode 100644 index 1e6a50f959..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/settings/FundsFoundDateInMillisStore.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.datasource.local.settings - -import com.tangem.datasource.local.datastore.LongSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -interface FundsFoundDateInMillisStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): Long? - - suspend fun store(item: Long) -} - -internal class DefaultFundsFoundDateInMillisStore( - store: LongSharedPreferencesDataStore, -) : FundsFoundDateInMillisStore, KeylessDataStoreDecorator( - wrappedDataStore = store, - key = "fundsFoundDate", -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/settings/UserInteractingStatusStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/settings/UserInteractingStatusStore.kt deleted file mode 100644 index 1a1ed3bf03..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/settings/UserInteractingStatusStore.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.datasource.local.settings - -import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -interface UserInteractingStatusStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): Boolean? - - suspend fun store(item: Boolean) -} - -internal class DefaultUserInteractingStatusStore( - store: BooleanSharedPreferencesDataStore, -) : UserInteractingStatusStore, KeylessDataStoreDecorator( - wrappedDataStore = store, - key = "userWasInteractWithRating", -) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/ShouldSaveUserWalletStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/ShouldSaveUserWalletStore.kt deleted file mode 100644 index cbc20ba792..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/ShouldSaveUserWalletStore.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.datasource.local.userwallet - -import com.tangem.datasource.local.datastore.BooleanSharedPreferencesDataStore -import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator -import kotlinx.coroutines.flow.Flow - -/** -[REDACTED_AUTHOR] - */ -interface ShouldSaveUserWalletStore { - - fun get(): Flow - - suspend fun getSyncOrNull(): Boolean? - - suspend fun store(item: Boolean) -} - -internal class DefaultShouldSaveUserWalletStore( - store: BooleanSharedPreferencesDataStore, -) : ShouldSaveUserWalletStore, KeylessDataStoreDecorator( - wrappedDataStore = store, - key = "saveUserWallets", -) \ No newline at end of file diff --git a/data/app-currency/build.gradle.kts b/data/app-currency/build.gradle.kts index 69613e2e85..ca214f10a9 100644 --- a/data/app-currency/build.gradle.kts +++ b/data/app-currency/build.gradle.kts @@ -23,12 +23,15 @@ dependencies { /** Project - Utils */ implementation(projects.core.utils) + /** AndroidX */ + implementation(deps.androidx.datastore) + /** DI */ implementation(deps.hilt.core) kapt(deps.hilt.kapt) /** Other */ + implementation(deps.jodatime) implementation(deps.kotlin.coroutines) implementation(deps.timber) - implementation(deps.jodatime) } \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt index 520be9c24d..bce54591e8 100644 --- a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt @@ -6,12 +6,16 @@ import com.tangem.data.common.cache.CacheRegistry import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObject +import com.tangem.datasource.local.preferences.utils.storeObject import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -19,23 +23,25 @@ import org.joda.time.Duration internal class DefaultAppCurrencyRepository( private val tangemTechApi: TangemTechApi, + private val appPreferencesStore: AppPreferencesStore, private val availableAppCurrenciesStore: AvailableAppCurrenciesStore, - private val selectedAppCurrencyStore: SelectedAppCurrencyStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, ) : AppCurrencyRepository { private val appCurrencyConverter = AppCurrencyConverter() - override fun getSelectedAppCurrency(): Flow = channelFlow { - launch(dispatchers.io) { - selectedAppCurrencyStore.get() - .map(appCurrencyConverter::convert) - .collect(::send) - } + override fun getSelectedAppCurrency(): Flow { + return channelFlow { + launch { + appPreferencesStore + .getObject(key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY) + .filterNotNull() + .map(appCurrencyConverter::convert) + .collect(::send) + } - withContext(dispatchers.io) { - if (selectedAppCurrencyStore.isEmpty()) { + withContext(dispatchers.io) { fetchDefaultAppCurrency() } } @@ -61,7 +67,10 @@ internal class DefaultAppCurrencyRepository( "Unable to find app currency with provided code: $currencyCode" } - selectedAppCurrencyStore.store(currency) + appPreferencesStore.storeObject( + key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY, + value = currency, + ) } } diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt index cd80c7c52b..d9cb9be5f7 100644 --- a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt @@ -4,7 +4,7 @@ import com.tangem.data.appcurrency.DefaultAppCurrencyRepository import com.tangem.data.common.cache.CacheRegistry import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -21,17 +21,17 @@ internal object AppCurrencyDataModule { @Singleton fun provideAppCurrencyRepository( tangemTechApi: TangemTechApi, + appPreferencesStore: AppPreferencesStore, availableAppCurrenciesStore: AvailableAppCurrenciesStore, - selectedAppCurrencyStore: SelectedAppCurrencyStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, ): AppCurrencyRepository { return DefaultAppCurrencyRepository( - tangemTechApi, - availableAppCurrenciesStore, - selectedAppCurrencyStore, - cacheRegistry, - dispatchers, + tangemTechApi = tangemTechApi, + appPreferencesStore = appPreferencesStore, + availableAppCurrenciesStore = availableAppCurrenciesStore, + cacheRegistry = cacheRegistry, + dispatchers = dispatchers, ) } } \ No newline at end of file diff --git a/data/app-theme/build.gradle.kts b/data/app-theme/build.gradle.kts index 7996a21c65..92a28083d7 100644 --- a/data/app-theme/build.gradle.kts +++ b/data/app-theme/build.gradle.kts @@ -27,4 +27,7 @@ dependencies { /** Other */ implementation(deps.kotlin.coroutines) + + /** Local storages */ + implementation(deps.androidx.datastore) } \ No newline at end of file diff --git a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/DefaultAppThemeModeRepository.kt b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/DefaultAppThemeModeRepository.kt index 4d8ed9cf4c..8abef1097b 100644 --- a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/DefaultAppThemeModeRepository.kt +++ b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/DefaultAppThemeModeRepository.kt @@ -1,35 +1,25 @@ package com.tangem.data.apptheme -import com.tangem.datasource.local.apptheme.AppThemeModeStore +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObject +import com.tangem.datasource.local.preferences.utils.storeObject import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.apptheme.repository.AppThemeModeRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.Flow internal class DefaultAppThemeModeRepository( - private val appThemeModeStore: AppThemeModeStore, - private val dispatchers: CoroutineDispatcherProvider, + private val appPreferencesStore: AppPreferencesStore, ) : AppThemeModeRepository { override fun getAppThemeMode(): Flow { - return channelFlow { - launch(dispatchers.io) { - if (appThemeModeStore.isEmpty()) { - appThemeModeStore.store(AppThemeMode.DEFAULT) - } - } - - launch(dispatchers.io) { - appThemeModeStore.get().collect(::send) - } - } + return appPreferencesStore.getObject( + key = PreferencesKeys.APP_THEME_MODE_KEY, + default = AppThemeMode.DEFAULT, + ) } override suspend fun changeAppThemeMode(mode: AppThemeMode) { - withContext(dispatchers.io) { - appThemeModeStore.store(mode) - } + appPreferencesStore.storeObject(key = PreferencesKeys.APP_THEME_MODE_KEY, value = mode) } } \ No newline at end of file diff --git a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt index a360ba19b6..11925c23cd 100644 --- a/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt +++ b/data/app-theme/src/main/kotlin/com/tangem/data/apptheme/di/AppThemeModeDataModule.kt @@ -1,9 +1,8 @@ package com.tangem.data.apptheme.di import com.tangem.data.apptheme.DefaultAppThemeModeRepository -import com.tangem.datasource.local.apptheme.AppThemeModeStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.apptheme.repository.AppThemeModeRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -16,10 +15,7 @@ internal object AppThemeModeDataModule { @Provides @Singleton - fun provideAppThemeModeRepository( - appThemeModeStore: AppThemeModeStore, - dispatchers: CoroutineDispatcherProvider, - ): AppThemeModeRepository { - return DefaultAppThemeModeRepository(appThemeModeStore, dispatchers) + fun provideAppThemeModeRepository(appPreferencesStore: AppPreferencesStore): AppThemeModeRepository { + return DefaultAppThemeModeRepository(appPreferencesStore = appPreferencesStore) } } \ No newline at end of file diff --git a/data/balance-hiding/build.gradle.kts b/data/balance-hiding/build.gradle.kts index 434d3e0a34..84db19b2d7 100644 --- a/data/balance-hiding/build.gradle.kts +++ b/data/balance-hiding/build.gradle.kts @@ -11,10 +11,10 @@ android { } dependencies { + implementation(deps.androidx.datastore) /** DI */ implementation(deps.hilt.android) - kapt(deps.hilt.kapt) implementation(deps.kotlin.coroutines) diff --git a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultBalanceHidingRepository.kt b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultBalanceHidingRepository.kt index 879bef5f17..948ec336de 100644 --- a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultBalanceHidingRepository.kt +++ b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultBalanceHidingRepository.kt @@ -1,36 +1,41 @@ package com.tangem.data.balancehiding -import com.tangem.datasource.local.appcurrency.BalanceHidingSettingsStore +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObject +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrDefault +import com.tangem.datasource.local.preferences.utils.storeObject import com.tangem.domain.balancehiding.BalanceHidingSettings import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.withContext internal class DefaultBalanceHidingRepository( - private val balanceHidingSettingsStore: BalanceHidingSettingsStore, - private val dispatchers: CoroutineDispatcherProvider, + private val appPreferencesStore: AppPreferencesStore, ) : BalanceHidingRepository { override fun getBalanceHidingSettingsFlow(): Flow { - return balanceHidingSettingsStore.get() - .onStart { emit(getBalanceHidingSettings()) } - .flowOn(dispatchers.io) - .distinctUntilChanged() + return appPreferencesStore.getObject( + key = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY, + default = DEFAULT_HIDING_SETTINGS, + ) } - override suspend fun storeBalanceHidingSettings(balanceHidingSettings: BalanceHidingSettings) { - withContext(dispatchers.io) { - balanceHidingSettingsStore.store(balanceHidingSettings) - } + override suspend fun storeBalanceHidingSettings(isBalanceHidden: BalanceHidingSettings) { + appPreferencesStore.storeObject(key = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY, value = isBalanceHidden) } override suspend fun getBalanceHidingSettings(): BalanceHidingSettings { - return withContext(dispatchers.io) { - balanceHidingSettingsStore.getSyncOrDefault() - } + return appPreferencesStore.getObjectSyncOrDefault( + key = PreferencesKeys.BALANCE_HIDING_SETTINGS_KEY, + default = DEFAULT_HIDING_SETTINGS, + ) + } + + private companion object { + val DEFAULT_HIDING_SETTINGS = BalanceHidingSettings( + isHidingEnabledInSettings = false, + isBalanceHidden = false, + isBalanceHidingNotificationEnabled = true, + ) } } \ No newline at end of file diff --git a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/di/BalanceHidingModule.kt b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/di/BalanceHidingModule.kt index 86b293b9fe..f83fac4932 100644 --- a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/di/BalanceHidingModule.kt +++ b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/di/BalanceHidingModule.kt @@ -3,16 +3,15 @@ package com.tangem.data.balancehiding.di import android.content.Context import com.tangem.data.balancehiding.DefaultBalanceHidingRepository import com.tangem.data.balancehiding.DefaultDeviceFlipDetector -import com.tangem.datasource.local.appcurrency.BalanceHidingSettingsStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.balancehiding.DeviceFlipDetector import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton -import dagger.hilt.android.qualifiers.ApplicationContext @Module @InstallIn(SingletonComponent::class) @@ -20,14 +19,8 @@ internal object BalanceHidingModule { @Provides @Singleton - fun provideBalanceHidingRepository( - balanceHidingSettingsStore: BalanceHidingSettingsStore, - dispatchers: CoroutineDispatcherProvider, - ): BalanceHidingRepository { - return DefaultBalanceHidingRepository( - balanceHidingSettingsStore = balanceHidingSettingsStore, - dispatchers = dispatchers, - ) + fun provideBalanceHidingRepository(appPreferencesStore: AppPreferencesStore): BalanceHidingRepository { + return DefaultBalanceHidingRepository(appPreferencesStore = appPreferencesStore) } @Provides diff --git a/data/card/build.gradle.kts b/data/card/build.gradle.kts index c7a692bd7e..6015c1ed3a 100644 --- a/data/card/build.gradle.kts +++ b/data/card/build.gradle.kts @@ -11,6 +11,8 @@ android { } dependencies { + implementation(deps.androidx.datastore) + implementation(deps.hilt.android) kapt(deps.hilt.kapt) diff --git a/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt index 6f4a1fb553..7f0a56fd9a 100644 --- a/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt +++ b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt @@ -1,43 +1,37 @@ package com.tangem.data.card import com.tangem.datasource.local.card.UsedCardInfo -import com.tangem.datasource.local.card.UsedCardsStore +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObject import com.tangem.domain.card.repository.CardRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.map internal class DefaultCardRepository( - private val usedCardsStore: UsedCardsStore, - private val dispatchers: CoroutineDispatcherProvider, + private val appPreferencesStore: AppPreferencesStore, ) : CardRepository { override fun wasCardScanned(cardId: String): Flow { - return channelFlow { - launch(dispatchers.io) { - usedCardsStore.get() - .collect { savedCards -> - send(element = savedCards.any { it.cardId == cardId }) - } + return appPreferencesStore.getObject>(key = PreferencesKeys.USED_CARDS_INFO_KEY) + .map { savedCards -> + savedCards?.any { it.cardId == cardId } ?: false } - - withContext(dispatchers.io) { - if (usedCardsStore.getSyncOrNull() == null) { - send(element = false) - } - } - } } override suspend fun setCardWasScanned(cardId: String) { - withContext(dispatchers.io) { - usedCardsStore.store( - item = usedCardsStore.getSyncOrNull() - ?.updateCard(cardId) - ?: listOf(UsedCardInfo(cardId = cardId, isScanned = true)), + appPreferencesStore.editData { mutablePreferences -> + val usedCards: List? = mutablePreferences.getObject( + key = PreferencesKeys.USED_CARDS_INFO_KEY, + ) + + val updatedUsedCards = usedCards?.updateCard(cardId) + ?: listOf(UsedCardInfo(cardId = cardId, isScanned = true)) + + mutablePreferences.setObject( + key = PreferencesKeys.USED_CARDS_INFO_KEY, + value = updatedUsedCards, ) } } diff --git a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt index 0c2cfdecbf..a526073cd2 100644 --- a/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt +++ b/data/card/src/main/java/com/tangem/data/card/di/CardDataModule.kt @@ -4,10 +4,9 @@ import com.tangem.data.card.DefaultCardRepository import com.tangem.data.card.DefaultCardSdkConfigRepository import com.tangem.data.card.sdk.CardSdkProvider import com.tangem.data.source.preferences.PreferencesDataSource -import com.tangem.datasource.local.card.UsedCardsStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -32,10 +31,7 @@ internal object CardDataModule { @Provides @Singleton - fun provideCardRepository( - usedCardsStore: UsedCardsStore, - dispatchers: CoroutineDispatcherProvider, - ): CardRepository { - return DefaultCardRepository(usedCardsStore = usedCardsStore, dispatchers = dispatchers) + fun provideCardRepository(appPreferencesStore: AppPreferencesStore): CardRepository { + return DefaultCardRepository(appPreferencesStore = appPreferencesStore) } } \ No newline at end of file diff --git a/data/settings/build.gradle.kts b/data/settings/build.gradle.kts index e59302acd9..5daa9b86f0 100644 --- a/data/settings/build.gradle.kts +++ b/data/settings/build.gradle.kts @@ -12,18 +12,19 @@ android { dependencies { + implementation(projects.core.datasource) + implementation(projects.core.utils) + + implementation(projects.data.source.preferences) + + implementation(projects.domain.balanceHiding.models) + implementation(projects.domain.settings) + + implementation(deps.androidx.datastore) + /** DI */ implementation(deps.hilt.android) - kapt(deps.hilt.kapt) implementation(deps.kotlin.coroutines) - - implementation(projects.core.utils) - implementation(projects.core.datasource) - - implementation(projects.domain.settings) - implementation(projects.domain.balanceHiding.models) - - implementation(projects.data.source.preferences) } diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultAppRatingRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultAppRatingRepository.kt index e7bc353372..78e99623f1 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/DefaultAppRatingRepository.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultAppRatingRepository.kt @@ -1,68 +1,42 @@ package com.tangem.data.settings -import com.tangem.datasource.local.settings.AppLaunchCountStore -import com.tangem.datasource.local.settings.AppRatingShowingCountStore -import com.tangem.datasource.local.settings.FundsFoundDateInMillisStore -import com.tangem.datasource.local.settings.UserInteractingStatusStore +import androidx.datastore.preferences.core.MutablePreferences +import androidx.datastore.preferences.core.edit +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.get import com.tangem.domain.settings.repositories.AppRatingRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.withContext import java.util.Calendar internal class DefaultAppRatingRepository( - private val fundsFoundDateInMillisStore: FundsFoundDateInMillisStore, - private val appLaunchCountStore: AppLaunchCountStore, - private val appRatingShowingCountStore: AppRatingShowingCountStore, - private val userInteractingStatusStore: UserInteractingStatusStore, - private val dispatchers: CoroutineDispatcherProvider, + private val appPreferencesStore: AppPreferencesStore, ) : AppRatingRepository { - override suspend fun initialize() { - fundsFoundDateInMillisStore.getSyncOrNull() - ?: fundsFoundDateInMillisStore.store(item = FUNDS_FOUND_DATE_UNDEFINED) - - appLaunchCountStore.getSyncOrNull() - ?: appLaunchCountStore.store(item = DEFAULT_APP_LAUNCH_COUNT) - - appRatingShowingCountStore.getSyncOrNull() - ?: appRatingShowingCountStore.store(item = FIRST_SHOWING_COUNT) - - userInteractingStatusStore.getSyncOrNull() - ?: userInteractingStatusStore.store(item = false) - } - override suspend fun setWalletWithFundsFound() { - withContext(dispatchers.io) { - val foundDate = fundsFoundDateInMillisStore.getSyncOrNull() - if (foundDate != null && foundDate != FUNDS_FOUND_DATE_UNDEFINED) return@withContext + appPreferencesStore.edit { mutablePreferences -> + val foundDate = mutablePreferences[PreferencesKeys.FUNDS_FOUND_DATE_KEY] + if (foundDate != null && foundDate != FUNDS_FOUND_DATE_UNDEFINED) return@edit - fundsFoundDateInMillisStore.store(item = Calendar.getInstance().timeInMillis) + mutablePreferences[PreferencesKeys.FUNDS_FOUND_DATE_KEY] = Calendar.getInstance().timeInMillis - val appLaunchCount = appLaunchCountStore.getSyncOrNull().let { count -> - if (count == null) { - appLaunchCountStore.store(item = DEFAULT_APP_LAUNCH_COUNT) - DEFAULT_APP_LAUNCH_COUNT - } else { - count - } - } - - appRatingShowingCountStore.store(item = appLaunchCount + FIRST_SHOWING_COUNT) - - userInteractingStatusStore.getSyncOrNull() - ?: userInteractingStatusStore.store(item = false) + val appLaunchCount = mutablePreferences[PreferencesKeys.APP_LAUNCH_COUNT_KEY] ?: DEFAULT_APP_LAUNCH_COUNT + mutablePreferences[PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY] = + appLaunchCount + FIRST_SHOWING_COUNT } } override fun isReadyToShow(): Flow { // TODO: [REDACTED_JIRA] return combine( - userInteractingStatusStore.get(), - appRatingShowingCountStore.get(), - appLaunchCountStore.get(), - fundsFoundDateInMillisStore.get(), + appPreferencesStore.get(key = PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY, default = false), + appPreferencesStore.get( + key = PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY, + default = FIRST_SHOWING_COUNT, + ), + appPreferencesStore.get(key = PreferencesKeys.APP_LAUNCH_COUNT_KEY, default = DEFAULT_APP_LAUNCH_COUNT), + appPreferencesStore.get(key = PreferencesKeys.FUNDS_FOUND_DATE_KEY, default = FUNDS_FOUND_DATE_UNDEFINED), ) { isInteracting, ratingShowingCount, appLaunchCount, fundsFoundDate -> if (!isInteracting) { val diff = Calendar.getInstance().timeInMillis - fundsFoundDate @@ -77,20 +51,22 @@ internal class DefaultAppRatingRepository( } override suspend fun remindLater() { - appLaunchCountStore.getSyncOrNull()?.let { - updateNextShowing(at = it + DEFER_SHOWING_COUNT) + appPreferencesStore.edit { mutablePreferences -> + mutablePreferences[PreferencesKeys.APP_LAUNCH_COUNT_KEY]?.let { + mutablePreferences.updateNextShowing(it + DEFER_SHOWING_COUNT) + } } } override suspend fun setNeverToShow() { - updateNextShowing(at = Int.MAX_VALUE) + appPreferencesStore.edit { mutablePreferences -> + mutablePreferences.updateNextShowing(at = Int.MAX_VALUE) + } } - private suspend fun updateNextShowing(at: Int) { - withContext(dispatchers.io) { - appRatingShowingCountStore.store(item = at) - userInteractingStatusStore.store(item = true) - } + private fun MutablePreferences.updateNextShowing(at: Int) { + this[PreferencesKeys.SHOW_RATING_DIALOG_AT_LAUNCH_COUNT_KEY] = at + this[PreferencesKeys.USER_WAS_INTERACT_WITH_RATING_KEY] = true } private companion object { diff --git a/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt index 68355c5d61..254235cf84 100644 --- a/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt +++ b/data/settings/src/main/java/com/tangem/data/settings/di/SettingsDataModule.kt @@ -3,10 +3,7 @@ package com.tangem.data.settings.di import com.tangem.data.settings.DefaultAppRatingRepository import com.tangem.data.settings.DefaultSettingsRepository import com.tangem.data.source.preferences.PreferencesDataSource -import com.tangem.datasource.local.settings.AppLaunchCountStore -import com.tangem.datasource.local.settings.AppRatingShowingCountStore -import com.tangem.datasource.local.settings.FundsFoundDateInMillisStore -import com.tangem.datasource.local.settings.UserInteractingStatusStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.settings.repositories.AppRatingRepository import com.tangem.domain.settings.repositories.SettingsRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -34,19 +31,7 @@ internal object SettingsDataModule { @Provides @Singleton - fun provideAppRatingRepository( - fundsFoundDateInMillisStore: FundsFoundDateInMillisStore, - appLaunchCountStore: AppLaunchCountStore, - appRatingShowingCountStore: AppRatingShowingCountStore, - userInteractingStatusStore: UserInteractingStatusStore, - dispatchers: CoroutineDispatcherProvider, - ): AppRatingRepository { - return DefaultAppRatingRepository( - fundsFoundDateInMillisStore = fundsFoundDateInMillisStore, - appLaunchCountStore = appLaunchCountStore, - appRatingShowingCountStore = appRatingShowingCountStore, - userInteractingStatusStore = userInteractingStatusStore, - dispatchers = dispatchers, - ) + fun provideAppRatingRepository(appPreferencesStore: AppPreferencesStore): AppRatingRepository { + return DefaultAppRatingRepository(appPreferencesStore = appPreferencesStore) } } \ No newline at end of file diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index d36f42d591..88d7471830 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -32,6 +32,9 @@ dependencies { implementation(deps.tangem.blockchain) implementation(deps.tangem.card.core) + /** AndroidX */ + implementation(deps.androidx.datastore) + /** DI */ implementation(deps.hilt.core) kapt(deps.hilt.kapt) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index f8e33206dd..80ceefbdc9 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -6,8 +6,8 @@ import com.tangem.data.tokens.repository.DefaultMarketCryptoCurrencyRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository import com.tangem.data.tokens.repository.DefaultQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore import com.tangem.datasource.local.network.NetworksStatusesStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.quote.QuotesStore import com.tangem.datasource.local.token.UserMarketCoinsStore import com.tangem.datasource.local.token.UserTokensStore @@ -52,15 +52,15 @@ internal object TokensDataModule { @Singleton fun provideQuotesRepository( tangemTechApi: TangemTechApi, + appPreferencesStore: AppPreferencesStore, quotesStore: QuotesStore, - selectedAppCurrencyStore: SelectedAppCurrencyStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, ): QuotesRepository { return DefaultQuotesRepository( tangemTechApi = tangemTechApi, + appPreferencesStore = appPreferencesStore, quotesStore = quotesStore, - selectedAppCurrencyStore = selectedAppCurrencyStore, cacheRegistry = cacheRegistry, dispatchers = dispatchers, ) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt index 37a89fafd0..825f937b65 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -5,7 +5,12 @@ import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.utils.QuotesConverter import com.tangem.data.tokens.utils.QuotesUnsupportedCurrenciesIdAdapter import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.datasource.local.* +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObject +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.datasource.local.quote.QuotesStore import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote @@ -17,8 +22,8 @@ import kotlinx.coroutines.withContext internal class DefaultQuotesRepository( private val tangemTechApi: TangemTechApi, + private val appPreferencesStore: AppPreferencesStore, private val quotesStore: QuotesStore, - private val selectedAppCurrencyStore: SelectedAppCurrencyStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, ) : QuotesRepository { @@ -31,8 +36,11 @@ internal class DefaultQuotesRepository( @OptIn(ExperimentalCoroutinesApi::class) override fun getQuotesUpdates(currenciesIds: Set): Flow> { - return selectedAppCurrencyStore.get() + return appPreferencesStore.getObject( + key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY, + ) .distinctUntilChanged() + .filterNotNull() .flatMapLatest { appCurrency -> fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh = false) @@ -44,9 +52,12 @@ internal class DefaultQuotesRepository( override suspend fun getQuotesSync(currenciesIds: Set, refresh: Boolean): Set { return withContext(dispatchers.io) { - val selectedAppCurrency = requireNotNull(selectedAppCurrencyStore.getSyncOrNull()) { - "Unable to get selected application currency to update quotes" - } + val selectedAppCurrency = requireNotNull( + value = appPreferencesStore.getObjectSyncOrNull( + key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY, + ), + lazyMessage = { "Unable to get selected application currency to update quotes" }, + ) fetchExpiredQuotes(currenciesIds, selectedAppCurrency.id, refresh) diff --git a/data/wallets/build.gradle.kts b/data/wallets/build.gradle.kts index f39f9b3c1e..c1a86f55ca 100644 --- a/data/wallets/build.gradle.kts +++ b/data/wallets/build.gradle.kts @@ -13,10 +13,12 @@ android { dependencies { implementation(projects.core.datasource) implementation(projects.core.utils) - implementation(projects.data.source.preferences) implementation(projects.domain.wallets) /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) + + /** Local storages */ + implementation(deps.androidx.datastore) } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index af326a1a45..2cc72cd098 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -1,29 +1,26 @@ package com.tangem.data.wallets -import com.tangem.datasource.local.userwallet.ShouldSaveUserWalletStore +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.get +import com.tangem.datasource.local.preferences.utils.getSyncOrDefault +import com.tangem.datasource.local.preferences.utils.store import com.tangem.domain.wallets.repository.WalletsRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.withContext internal class DefaultWalletsRepository( - private val shouldSaveUserWalletStore: ShouldSaveUserWalletStore, - private val dispatchers: CoroutineDispatcherProvider, + private val appPreferencesStore: AppPreferencesStore, ) : WalletsRepository { - override suspend fun initialize() { - withContext(dispatchers.io) { - shouldSaveUserWalletStore.getSyncOrNull() ?: shouldSaveUserWalletStore.store(item = false) - } - } - override suspend fun shouldSaveUserWalletsSync(): Boolean { - return withContext(dispatchers.io) { shouldSaveUserWalletStore.getSyncOrNull() ?: false } + return appPreferencesStore.getSyncOrDefault(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false) } - override fun shouldSaveUserWallets(): Flow = shouldSaveUserWalletStore.get() + override fun shouldSaveUserWallets(): Flow { + return appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false) + } override suspend fun saveShouldSaveUserWallets(item: Boolean) { - withContext(dispatchers.io) { shouldSaveUserWalletStore.store(item = item) } + appPreferencesStore.store(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, value = item) } } \ No newline at end of file diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index 80ff2c2567..29cd5fb5c0 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -1,9 +1,8 @@ package com.tangem.data.wallets.di import com.tangem.data.wallets.DefaultWalletsRepository -import com.tangem.datasource.local.userwallet.ShouldSaveUserWalletStore +import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.domain.wallets.repository.WalletsRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -12,17 +11,11 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object WalletsDataModule { +internal object WalletsDataModule { @Provides @Singleton - fun providesWalletsRepository( - shouldSaveUserWalletStore: ShouldSaveUserWalletStore, - coroutineDispatcherProvider: CoroutineDispatcherProvider, - ): WalletsRepository { - return DefaultWalletsRepository( - shouldSaveUserWalletStore = shouldSaveUserWalletStore, - dispatchers = coroutineDispatcherProvider, - ) + fun providesWalletsRepository(appPreferencesStore: AppPreferencesStore): WalletsRepository { + return DefaultWalletsRepository(appPreferencesStore = appPreferencesStore) } } \ No newline at end of file diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/AppRatingRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/AppRatingRepository.kt index e10dc94046..33772c3cf6 100644 --- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/AppRatingRepository.kt +++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/AppRatingRepository.kt @@ -4,9 +4,6 @@ import kotlinx.coroutines.flow.Flow interface AppRatingRepository { - // FIXME: We must to initialize all stores before calling [isReadyToShow], otherwise flow will not emit data - suspend fun initialize() - suspend fun setWalletWithFundsFound() fun isReadyToShow(): Flow diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt index 6190b6d2e0..003f9b9ff2 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsRepository.kt @@ -4,8 +4,6 @@ import kotlinx.coroutines.flow.Flow interface WalletsRepository { - suspend fun initialize() - suspend fun shouldSaveUserWalletsSync(): Boolean fun shouldSaveUserWallets(): Flow From d574159e14d0e5a9f8045f6b006eb3c83e4dcb45 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Nov 2023 14:28:16 +0200 Subject: [PATCH 3/6] Updated on 2026-08-14 --- .../tangem/feature/swap/SwapRepositoryImpl.kt | 26 +++++++++++++++++++ .../swap/converters/RateTypeConverter.kt | 22 ++++++++++++++++ .../swap/converters/SwapPairInfoConverter.kt | 14 +++------- .../domain/DefaultBlockchainInteractor.kt | 2 +- .../feature/swap/domain/SwapInteractorImpl.kt | 12 +++++---- .../feature/swap/domain/SwapRepository.kt | 10 +++++++ .../domain/models/domain/ExchangeQuote.kt | 8 ++++++ .../domain/models/domain/SwapPairLeast.kt | 20 +++++--------- .../models/ui/TokensDataStateExpress.kt | 6 ++--- .../feature/swap/viewmodels/SwapViewModel.kt | 17 +++++++----- 10 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 features/swap/data/src/main/java/com/tangem/feature/swap/converters/RateTypeConverter.kt create mode 100644 features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index ce4a54b96e..9eb5bedf15 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -46,6 +46,7 @@ internal class SwapRepositoryImpl @Inject constructor( private val swapConverter = SwapConverter() private val leastTokenInfoConverter = LeastTokenInfoConverter() private val swapPairInfoConverter = SwapPairInfoConverter() + private val rateTypeConverter = RateTypeConverter() override suspend fun getPairs( initialCurrency: LeastTokenInfo, @@ -257,6 +258,31 @@ internal class SwapRepositoryImpl @Inject constructor( return oneInchApiFactory.getApi(networkId) } + override suspend fun getExchangeQuote( + fromContractAddress: String, + fromNetwork: String, + toContractAddress: String, + toNetwork: String, + fromAmount: BigDecimal, + providerId: Int, + rateType: RateType + ): ExchangeQuote { + val response = tangemExpressApi.getExchangeQuote( + fromContractAddress, + fromNetwork, + toContractAddress, + toNetwork, + fromAmount, + providerId, + rateTypeConverter.convertBack(rateType) + ).getOrThrow() + + return ExchangeQuote( + toAmount = response.toAmount, + allowanceContract = response.allowanceContract + ) + } + companion object { // TODO("get this ids from blockchain enum later") private const val OPTIMISM_ID = "optimistic-ethereum" diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/RateTypeConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/RateTypeConverter.kt new file mode 100644 index 0000000000..0d25c4eca2 --- /dev/null +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/RateTypeConverter.kt @@ -0,0 +1,22 @@ +package com.tangem.feature.swap.converters + +import com.tangem.datasource.api.express.models.response.RateType +import com.tangem.utils.converter.TwoWayConverter +import com.tangem.feature.swap.domain.models.domain.RateType as RateTypeDomain + +class RateTypeConverter : TwoWayConverter { + + override fun convert(value: RateType): RateTypeDomain { + return when (value) { + RateType.FIXED -> RateTypeDomain.FIXED + RateType.FLOAT -> RateTypeDomain.FLOAT + } + } + + override fun convertBack(value: RateTypeDomain): RateType { + return when (value) { + RateTypeDomain.FIXED -> RateType.FIXED + RateTypeDomain.FLOAT -> RateType.FLOAT + } + } +} \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt index 627a7a8fb4..e1e48fc630 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt @@ -1,16 +1,16 @@ package com.tangem.feature.swap.converters -import com.tangem.datasource.api.express.models.response.RateType import com.tangem.datasource.api.express.models.response.SwapPair import com.tangem.datasource.api.express.models.response.SwapPairProvider import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo import com.tangem.feature.swap.domain.models.domain.SwapPairLeast as SwapPairDomain -import com.tangem.feature.swap.domain.models.domain.SwapPairProvider as SwapPairProviderDomain -import com.tangem.feature.swap.domain.models.domain.RateType as RateTypeDomain +import com.tangem.feature.swap.domain.models.domain.SwapProvider as SwapPairProviderDomain import com.tangem.utils.converter.Converter class SwapPairInfoConverter : Converter { + private val rateTypeConverter = RateTypeConverter() + override fun convert(value: SwapPair): SwapPairDomain { return SwapPairDomain( from = LeastTokenInfo( @@ -30,14 +30,8 @@ class SwapPairInfoConverter : Converter { private fun convertProvider(swapPairProvider: SwapPairProvider): SwapPairProviderDomain { return SwapPairProviderDomain( providerId = swapPairProvider.providerId, - rateTypes = swapPairProvider.rateTypes.map { convertRateType(it) }, + rateTypes = swapPairProvider.rateTypes.map { rateTypeConverter.convert(it) }, ) } - private fun convertRateType(rateType: RateType): RateTypeDomain { - return when (rateType) { - RateType.FIXED -> RateTypeDomain.FIXED - RateType.FLOAT -> RateTypeDomain.FLOAT - } - } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultBlockchainInteractor.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultBlockchainInteractor.kt index 0b9605485b..cb99b58076 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultBlockchainInteractor.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/DefaultBlockchainInteractor.kt @@ -27,7 +27,7 @@ internal class DefaultBlockchainInteractor @Inject constructor( return if (token is CryptoCurrency.Token) { token.decimals } else { - transactionManager.getNativeTokenDecimals(token.network.id.value) + transactionManager.getNativeTokenDecimals(token.network.backendId) } } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 3e63caec40..738424d73c 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -107,15 +107,17 @@ internal class SwapInteractorImpl @Inject constructor( tokenInfoForFilter(it).network == currency.network.backendId } - val availableCryptoCurrencies = filteredPairs.mapNotNull { - findCryptoCurrencyStatusByLeastInfo(tokenInfoForAvailable(it), cryptoCurrenciesList) + val availableCryptoCurrencies = filteredPairs.mapNotNull { pair -> + val status = findCryptoCurrencyStatusByLeastInfo(tokenInfoForAvailable(pair), cryptoCurrenciesList) + status?.let { CryptoCurrencySwapInfo(it, pair.providers) } } - val unavailableCryptoCurrencies = cryptoCurrenciesList - availableCryptoCurrencies.toSet() + val unavailableCryptoCurrencies = + (cryptoCurrenciesList - availableCryptoCurrencies.map { it.currencyStatus }.toSet()) return CurrenciesGroup( available = availableCryptoCurrencies, - unavailable = unavailableCryptoCurrencies, + unavailable = unavailableCryptoCurrencies.map { CryptoCurrencySwapInfo(it, emptyList()) } ) } @@ -415,7 +417,7 @@ internal class SwapInteractorImpl @Inject constructor( return if (token is CryptoCurrency.Token) { token.decimals } else { - transactionManager.getNativeTokenDecimals(token.network.id.value) + transactionManager.getNativeTokenDecimals(token.network.backendId) } } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt index ab0e4095c8..8942e75161 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt @@ -61,4 +61,14 @@ interface SwapRepository { currency: CryptoCurrency, amount: BigDecimal?, ): String + + suspend fun getExchangeQuote( + fromContractAddress: String, + fromNetwork: String, + toContractAddress: String, + toNetwork: String, + fromAmount: BigDecimal, + providerId: Int, + rateType: RateType, + ) : ExchangeQuote } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt new file mode 100644 index 0000000000..d087b97955 --- /dev/null +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt @@ -0,0 +1,8 @@ +package com.tangem.feature.swap.domain.models.domain + +import java.math.BigDecimal + +data class ExchangeQuote( + val toAmount: BigDecimal, + val allowanceContract: String? +) \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt index 704a27dc1b..a41f3d283d 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt @@ -1,6 +1,6 @@ package com.tangem.feature.swap.domain.models.domain -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus /** * Domain layer representation of SwapPair data network model. @@ -12,20 +12,12 @@ import com.tangem.domain.tokens.model.CryptoCurrency data class SwapPairLeast( val from: LeastTokenInfo, val to: LeastTokenInfo, - val providers: List, + val providers: List, ) -/** - * Enriched model of swap pair data. Contains full CryptoCurrency models instead of least info. - * - * @property from CryptoCurrency we want to change - * @property to CryptoCurrency we want to exchange for - * @property providers Exchange providers - */ -data class SwapPair( - val from: CryptoCurrency, - val to: CryptoCurrency, - val providers: List, +data class CryptoCurrencySwapInfo( + val currencyStatus: CryptoCurrencyStatus, + val providers: List ) /** @@ -34,7 +26,7 @@ data class SwapPair( * @property providerId provider id * @property rateTypes supported rate types */ -data class SwapPairProvider( +data class SwapProvider( val providerId: Int, val rateTypes: List, ) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TokensDataStateExpress.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TokensDataStateExpress.kt index 2401c08574..752e9184a7 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TokensDataStateExpress.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TokensDataStateExpress.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain.models.ui import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo data class TokensDataStateExpress( val fromGroup: CurrenciesGroup, @@ -9,8 +9,8 @@ data class TokensDataStateExpress( ) data class CurrenciesGroup( - val available: List, - val unavailable: List, + val available: List, + val unavailable: List, ) data class FoundTokensStateExpress( diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 177f462107..99919c05a3 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -132,14 +132,17 @@ internal class SwapViewModel @Inject constructor( runCatching(dispatchers.io) { swapInteractor.getTokensDataState(initialCryptoCurrency) }.onSuccess { state -> - // dataState = dataState.copy( - // ) + dataState = dataState.copy( + fromCryptoCurrency = initialCryptoCurrency, + toCryptoCurrency = state.toGroup.available.first().currencyStatus.currency + ) + // updateTokensState(dataState = state.foundTokensState) - // startLoadingQuotes( - // fromToken = state.preselectTokens.fromToken, - // toToken = state.preselectTokens.toToken, - // amount = lastAmount.value, - // ) + startLoadingQuotes( + fromToken = initialCryptoCurrency, + toToken = state.toGroup.available.first().currencyStatus.currency, + amount = lastAmount.value, + ) }.onFailure { Timber.tag(loggingTag).e(it) } From 5a277755c415cc0ced047944eac387cbcbddb73c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 Nov 2023 10:53:09 +0300 Subject: [PATCH 4/6] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../tangem/datasource/local/quote/DefaultQuotesStore.kt | 8 ++++++++ .../java/com/tangem/datasource/local/quote/QuotesStore.kt | 2 ++ .../data/tokens/repository/DefaultQuotesRepository.kt | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index ade38b689e..f5a90466d2 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit ade38b689e5e390cf4105f9dfa641b4f6912dcd0 +Subproject commit f5a90466d2245ca5cfde59b49dc7fa82a6b7cbc2 diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt index 2b8de0afd9..fc0d1e0e42 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt @@ -30,6 +30,14 @@ internal class DefaultQuotesStore( } } + override suspend fun getSync(currenciesIds: Set): Set { + return currenciesIds.mapNotNull { currencyId -> + currencyId.rawCurrencyId?.let { + dataStore.getSyncOrNull(it) + } + }.toSet() + } + override suspend fun store(response: QuotesResponse) { val quotes = response.quotes.mapValues { (id, quote) -> StoredQuote(id, quote) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt index 64b3bf2c9c..dd3f577782 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt @@ -9,5 +9,7 @@ interface QuotesStore { fun get(currenciesIds: Set): Flow> + suspend fun getSync(currenciesIds: Set): Set + suspend fun store(response: QuotesResponse) } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt index 825f937b65..cdd04c62ce 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -61,7 +61,7 @@ internal class DefaultQuotesRepository( fetchExpiredQuotes(currenciesIds, selectedAppCurrency.id, refresh) - val quotes = quotesStore.get(currenciesIds).first() + val quotes = quotesStore.getSync(currenciesIds) quotesConverter.convertSet(quotes) } From 616c0b2c05e9b07c4442fcf1263544b97e1351f9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 20 Nov 2023 22:33:11 +0300 Subject: [PATCH 5/6] Updated on 2026-08-14 --- .../middlewares/TradeCryptoMiddleware.kt | 4 +- .../local/quote/DefaultQuotesStore.kt | 21 +++--- ...kt => GetCryptoCurrencyStatusesUseCase.kt} | 22 +----- .../CurrenciesStatusesOperations.kt | 30 ++++++++ .../tangem/feature/swap/SwapRepositoryImpl.kt | 6 +- .../swap/converters/SwapPairInfoConverter.kt | 1 - .../feature/swap/domain/SwapInteractorImpl.kt | 25 +++---- .../feature/swap/domain/SwapRepository.kt | 3 +- .../swap/domain/di/SwapDomainModule.kt | 8 +- .../domain/models/domain/ExchangeQuote.kt | 2 +- .../domain/models/domain/SwapPairLeast.kt | 2 +- features/swap/presentation/build.gradle.kts | 3 + .../swap/converters/TokensDataConverter.kt | 74 +++++++++++++------ .../swap/models/SwapSelectTokenStateHolder.kt | 10 +-- .../tangem/feature/swap/ui/StateBuilder.kt | 19 ++--- .../feature/swap/ui/SwapSelectTokenScreen.kt | 13 ++-- .../swap/viewmodels/SwapProcessDataState.kt | 2 + .../feature/swap/viewmodels/SwapViewModel.kt | 51 +++++++++---- 18 files changed, 173 insertions(+), 123 deletions(-) rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/{GetCryptoCurrencyStatusUseCase.kt => GetCryptoCurrencyStatusesUseCase.kt} (65%) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 66027e8a6b..80f84ebf33 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -36,8 +36,6 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.launch -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json @Suppress("LargeClass") class TradeCryptoMiddleware { @@ -259,7 +257,7 @@ class TradeCryptoMiddleware { private fun openSwap(currency: CryptoCurrency) { val bundle = bundleOf( - SwapFragment.CURRENCY_BUNDLE_KEY to currency + SwapFragment.CURRENCY_BUNDLE_KEY to currency, ) store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle)) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt index fc0d1e0e42..e28a90bfb2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt @@ -4,8 +4,8 @@ import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.quote.model.StoredQuote import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.utils.extensions.addOrReplace -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow internal class DefaultQuotesStore( private val dataStore: StringKeyDataStore, @@ -13,20 +13,17 @@ internal class DefaultQuotesStore( override fun get(currenciesIds: Set): Flow> { return channelFlow { - val flows = currenciesIds.mapNotNull { currencyId -> - currencyId.rawCurrencyId?.let(dataStore::get) - } - - if (dataStore.isEmpty() || flows.isEmpty()) { + if (dataStore.isEmpty()) { send(emptySet()) } - merge(*flows.toTypedArray()) - .scan>(emptySet()) { acc, quote -> - acc.addOrReplace(quote) { it.rawCurrencyId == quote.rawCurrencyId } + val quotes = currenciesIds.mapNotNull { currencyId -> + currencyId.rawCurrencyId?.let { + dataStore.getSyncOrNull(it) } - .filter(Set::isNotEmpty) - .collect(::send) + } + + send(quotes.toSet()) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt similarity index 65% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusUseCase.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt index d917a747c7..2734f1b2e3 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt @@ -4,9 +4,7 @@ import arrow.core.Either import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -import com.tangem.domain.tokens.operations.TokenListOperations import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository @@ -14,7 +12,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* -class GetCryptoCurrencyStatusUseCase( +class GetCryptoCurrencyStatusesUseCase( internal val currenciesRepository: CurrenciesRepository, internal val quotesRepository: QuotesRepository, internal val networksRepository: NetworksRepository, @@ -29,25 +27,9 @@ class GetCryptoCurrencyStatusUseCase( networksRepository = networksRepository, ) - return operations.getCurrenciesStatusesFlow() + return operations.getCurrenciesStatusesMergedFlow() .map { maybeCurrenciesStatuses -> maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError) } } - - @Suppress("UnusedPrivateMember") - private fun createTokenList( - userWalletId: UserWalletId, - tokens: List, - ): Flow> { - val operations = TokenListOperations( - userWalletId = userWalletId, - tokens = tokens, - currenciesRepository = currenciesRepository, - ) - - return operations.getTokenListFlow().map { maybeTokenList -> - maybeTokenList.mapLeft(TokenListOperations.Error::mapToTokenListError) - } - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 9bce6f0d35..60e3d3b4b0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -67,6 +67,36 @@ internal class CurrenciesStatusesOperations( } } + fun getCurrenciesStatusesMergedFlow(): Flow>> { + return getMultiCurrencyWalletCurrencies().transformLatest { maybeCurrencies -> + val nonEmptyCurrencies = maybeCurrencies.fold( + ifLeft = { error -> + emit(error.left()) + return@transformLatest + }, + ifRight = List::toNonEmptyListOrNull, + ) + + if (nonEmptyCurrencies == null) { + val emptyCurrenciesStatuses = emptyList() + + emit(emptyCurrenciesStatuses.right()) + return@transformLatest + } + + val (networks, currenciesIds) = getIds(nonEmptyCurrencies) + + val currenciesFlow = combine( + getQuotes(currenciesIds), + getNetworksStatuses(networks), + ) { maybeQuotes, maybeNetworksStatuses -> + createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses) + } + + emitAll(currenciesFlow) + } + } + fun getCardCurrenciesStatusesFlow(): Flow>> { return flow { val nonEmptyCurrencies = recover( diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt index 9eb5bedf15..9aa4fc368b 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/SwapRepositoryImpl.kt @@ -265,7 +265,7 @@ internal class SwapRepositoryImpl @Inject constructor( toNetwork: String, fromAmount: BigDecimal, providerId: Int, - rateType: RateType + rateType: RateType, ): ExchangeQuote { val response = tangemExpressApi.getExchangeQuote( fromContractAddress, @@ -274,12 +274,12 @@ internal class SwapRepositoryImpl @Inject constructor( toNetwork, fromAmount, providerId, - rateTypeConverter.convertBack(rateType) + rateTypeConverter.convertBack(rateType), ).getOrThrow() return ExchangeQuote( toAmount = response.toAmount, - allowanceContract = response.allowanceContract + allowanceContract = response.allowanceContract, ) } diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt index e1e48fc630..c205bec15e 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/SwapPairInfoConverter.kt @@ -33,5 +33,4 @@ class SwapPairInfoConverter : Converter { rateTypes = swapPairProvider.rateTypes.map { rateTypeConverter.convert(it) }, ) } - } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 738424d73c..f9d1faa5bb 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain import arrow.core.getOrElse import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.GetCryptoCurrencyStatusUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyStatusesUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Network @@ -39,7 +39,7 @@ internal class SwapInteractorImpl @Inject constructor( private val networksRepository: NetworksRepository, private val walletFeatureToggles: WalletFeatureToggles, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusUseCase, + private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesUseCase, ) : SwapInteractor { // TODO: Move to DI @@ -58,7 +58,7 @@ internal class SwapInteractorImpl @Inject constructor( ifRight = { it }, ) - requireNotNull(selectedWallet) + requireNotNull(selectedWallet) { "No selected wallet" } val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase(selectedWallet.walletId) .first() @@ -112,12 +112,13 @@ internal class SwapInteractorImpl @Inject constructor( status?.let { CryptoCurrencySwapInfo(it, pair.providers) } } - val unavailableCryptoCurrencies = - (cryptoCurrenciesList - availableCryptoCurrencies.map { it.currencyStatus }.toSet()) + val unavailableCryptoCurrencies = cryptoCurrenciesList - availableCryptoCurrencies + .map { it.currencyStatus } + .toSet() return CurrenciesGroup( available = availableCryptoCurrencies, - unavailable = unavailableCryptoCurrencies.map { CryptoCurrencySwapInfo(it, emptyList()) } + unavailable = unavailableCryptoCurrencies.map { CryptoCurrencySwapInfo(it, emptyList()) }, ) } @@ -138,14 +139,10 @@ internal class SwapInteractorImpl @Inject constructor( } } - private fun Currency.getContractAddress(): String { - return when (this) { - is Currency.NativeToken -> "0" - is Currency.NonNativeToken -> this.contractAddress - } - } - - suspend fun getPairs(initialCurrency: LeastTokenInfo, currenciesList: List): List { + private suspend fun getPairs( + initialCurrency: LeastTokenInfo, + currenciesList: List, + ): List { return repository.getPairs(initialCurrency, currenciesList) } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt index 8942e75161..d64b0fa8f0 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapRepository.kt @@ -62,6 +62,7 @@ interface SwapRepository { amount: BigDecimal?, ): String + @Suppress("LongParameterList") suspend fun getExchangeQuote( fromContractAddress: String, fromNetwork: String, @@ -70,5 +71,5 @@ interface SwapRepository { fromAmount: BigDecimal, providerId: Int, rateType: RateType, - ) : ExchangeQuote + ): ExchangeQuote } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index dc882859ae..3169f16427 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain.di import com.tangem.domain.tokens.GetCardTokensListUseCase import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase -import com.tangem.domain.tokens.GetCryptoCurrencyStatusUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyStatusesUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository @@ -35,7 +35,7 @@ class SwapDomainModule { networksRepository: NetworksRepository, walletFeatureToggles: WalletFeatureToggles, @SwapScope getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - @SwapScope getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusUseCase, + @SwapScope getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesUseCase, ): SwapInteractor { return SwapInteractorImpl( transactionManager = transactionManager, @@ -81,8 +81,8 @@ class SwapDomainModule { quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, - ): GetCryptoCurrencyStatusUseCase { - return GetCryptoCurrencyStatusUseCase( + ): GetCryptoCurrencyStatusesUseCase { + return GetCryptoCurrencyStatusesUseCase( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt index d087b97955..ee352e70c4 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/ExchangeQuote.kt @@ -4,5 +4,5 @@ import java.math.BigDecimal data class ExchangeQuote( val toAmount: BigDecimal, - val allowanceContract: String? + val allowanceContract: String?, ) \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt index a41f3d283d..ee772becce 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapPairLeast.kt @@ -17,7 +17,7 @@ data class SwapPairLeast( data class CryptoCurrencySwapInfo( val currencyStatus: CryptoCurrencyStatus, - val providers: List + val providers: List, ) /** diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index 1068c49d28..8be06eaf61 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -17,6 +17,8 @@ dependencies { implementation(projects.common) /** Domain modules **/ + implementation(projects.domain.appCurrency) + implementation(projects.domain.appCurrency.models) implementation(projects.domain.balanceHiding) implementation(projects.domain.balanceHiding.models) @@ -28,6 +30,7 @@ dependencies { implementation(deps.androidx.browser) /** Compose */ + implementation(deps.arrow.core) implementation(deps.compose.foundation) implementation(deps.compose.material) implementation(deps.compose.ui.tooling) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index bb52e2b01d..03047e24b8 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -2,52 +2,80 @@ package com.tangem.feature.swap.converters import com.tangem.common.Provider import com.tangem.core.ui.components.currency.tokenicon.TokenIconState -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.feature.swap.domain.models.domain.NetworkInfo -import com.tangem.feature.swap.domain.models.ui.FoundTokensStateExpress -import com.tangem.feature.swap.domain.models.ui.TokenWithBalanceExpress -import com.tangem.feature.swap.models.Network +import com.tangem.core.ui.extensions.networkIconResId +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo +import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup import com.tangem.feature.swap.models.SwapSelectTokenStateHolder import com.tangem.feature.swap.models.TokenBalanceData import com.tangem.feature.swap.models.TokenToSelectState +import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toImmutableList class TokensDataConverter( private val onSearchEntered: (String) -> Unit, private val onTokenSelected: (String) -> Unit, private val isBalanceHiddenProvider: Provider, -) { + private val appCurrencyProvider: Provider, +) : Converter { - fun convertWithNetwork(value: FoundTokensStateExpress, network: NetworkInfo): SwapSelectTokenStateHolder { + override fun convert(value: CurrenciesGroup): SwapSelectTokenStateHolder { + val availableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource + val unavailableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource return SwapSelectTokenStateHolder( - availableTokens = value.tokensInWallet.map { tokenWithBalanceToTokenToSelect(it) }.toImmutableList(), - unavailableTokens = value.loadedTokens.map { tokenWithBalanceToTokenToSelect(it) }.toImmutableList(), + availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it) } + .toMutableList() + .apply { + this.add(0, availableTitle) + } + .toImmutableList(), + unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it) } + .toMutableList() + .apply { + this.add(0, unavailableTitle) + } + .toImmutableList(), onSearchEntered = onSearchEntered, onTokenSelected = onTokenSelected, - network = Network(network.name, network.blockchainId), ) } - private fun tokenWithBalanceToTokenToSelect( - tokenWithBalance: TokenWithBalanceExpress, - ): TokenToSelectState.TokenToSelect { + private fun tokenWithBalanceToTokenToSelect(cryptoCurrencySwapInfo: CryptoCurrencySwapInfo): TokenToSelectState { + val cryptoCurrencyStatus = cryptoCurrencySwapInfo.currencyStatus return TokenToSelectState.TokenToSelect( - id = tokenWithBalance.token.id.value, - name = tokenWithBalance.token.name, - symbol = tokenWithBalance.token.symbol, - isNative = tokenWithBalance.token is CryptoCurrency.Coin, - // todo replace converting + id = cryptoCurrencyStatus.currency.id.value, + name = cryptoCurrencyStatus.currency.name, + symbol = cryptoCurrencyStatus.currency.symbol, tokenIcon = TokenIconState.CoinIcon( - url = "", - fallbackResId = 0, + url = cryptoCurrencyStatus.currency.iconUrl, + fallbackResId = cryptoCurrencyStatus.currency.networkIconResId, isGrayscale = false, - showCustomBadge = false, + showCustomBadge = cryptoCurrencyStatus.currency.isCustom, ), addedTokenBalanceData = TokenBalanceData( - amount = tokenWithBalance.tokenBalanceData?.amount, - amountEquivalent = tokenWithBalance.tokenBalanceData?.amountEquivalent, + amount = formatCryptoAmount(cryptoCurrencyStatus), + amountEquivalent = formatFiatAmount(cryptoCurrencyStatus, appCurrencyProvider.invoke()), isBalanceHidden = isBalanceHiddenProvider.invoke(), ), ) } + + private fun formatCryptoAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): String { + return BigDecimalFormatter.formatCryptoAmount( + cryptoCurrencyStatus.value.amount, + cryptoCurrencyStatus.currency.symbol, + cryptoCurrencyStatus.currency.decimals, + ) + } + + private fun formatFiatAmount(cryptoCurrencyStatus: CryptoCurrencyStatus, appCurrency: AppCurrency): String { + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = cryptoCurrencyStatus.value.fiatAmount, + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + ) + } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt index 382da8e588..ac6762d7b7 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt @@ -1,30 +1,24 @@ package com.tangem.feature.swap.models import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.extensions.TextReference import kotlinx.collections.immutable.ImmutableList data class SwapSelectTokenStateHolder( val availableTokens: ImmutableList, val unavailableTokens: ImmutableList, - val network: Network, val onSearchEntered: (String) -> Unit, val onTokenSelected: (String) -> Unit, ) -data class Network( - val name: String, - val blockchainId: String, -) - sealed class TokenToSelectState { - data class Title(val title: String) : TokenToSelectState() + data class Title(val title: TextReference) : TokenToSelectState() data class TokenToSelect( val id: String, val name: String, val symbol: String, - val isNative: Boolean, val tokenIcon: TokenIconState, val available: Boolean = true, val addedTokenBalanceData: TokenBalanceData? = null, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index bea53175af..510502e088 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -9,6 +9,7 @@ import com.tangem.core.ui.components.states.SelectableItemsState import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.feature.swap.converters.TokensDataConverter import com.tangem.feature.swap.domain.models.DataError @@ -24,12 +25,17 @@ import kotlinx.collections.immutable.toImmutableList * State builder creates a specific states for SwapScreen */ @Suppress("LargeClass") -internal class StateBuilder(val actions: UiActions, val isBalanceHiddenProvider: Provider) { +internal class StateBuilder( + private val actions: UiActions, + private val isBalanceHiddenProvider: Provider, + appCurrencyProvider: Provider, +) { private val tokensDataConverter = TokensDataConverter( onSearchEntered = actions.onSearchEntered, onTokenSelected = actions.onTokenSelected, isBalanceHiddenProvider = isBalanceHiddenProvider, + appCurrencyProvider = appCurrencyProvider, ) fun createInitialLoadingState(initialCurrency: CryptoCurrency, networkInfo: NetworkInfo): SwapStateHolder { @@ -247,15 +253,10 @@ internal class StateBuilder(val actions: UiActions, val isBalanceHiddenProvider: ) } - fun addTokensToState( - uiState: SwapStateHolder, - dataState: FoundTokensStateExpress, - networkInfo: NetworkInfo, - ): SwapStateHolder { + fun addTokensToState(uiState: SwapStateHolder, tokensDataState: CurrenciesGroup): SwapStateHolder { return uiState.copy( - selectTokenState = tokensDataConverter.convertWithNetwork( - value = dataState, - network = networkInfo, + selectTokenState = tokensDataConverter.convert( + value = tokensDataState, ), ) } 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 1faa262bb3..2bbeedc808 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 @@ -11,7 +11,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.common.Strings @@ -20,7 +19,8 @@ import com.tangem.core.ui.components.appbar.ExpandableSearchView import com.tangem.core.ui.components.currency.tokenicon.TokenIcon import com.tangem.core.ui.components.currency.tokenicon.TokenIconState import com.tangem.core.ui.decorations.roundedShapeItemDecoration -import com.tangem.core.ui.extensions.getActiveIconRes +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.swap.models.* import com.tangem.feature.swap.presentation.R @@ -43,8 +43,7 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit) placeholderSearchText = stringResource(id = R.string.common_search_tokens), onSearchChange = state.onSearchEntered, onSearchDisplayClose = { state.onSearchEntered("") }, - subtitle = state.network.name, - icon = painterResource(id = getActiveIconRes(state.network.blockchainId)), + subtitle = "", // todo add title ) }, ) @@ -112,7 +111,7 @@ private fun TitleHeader(item: TokenToSelectState.Title, modifier: Modifier = Mod .background(TangemTheme.colors.background.action), ) { Text( - text = item.title, + text = item.title.resolveReference(), style = TangemTheme.typography.overline, modifier = Modifier .padding( @@ -220,7 +219,6 @@ private val token = TokenToSelectState.TokenToSelect( id = "", name = "USDC", symbol = "USDC", - isNative = false, addedTokenBalanceData = TokenBalanceData( amount = "15 000 $", amountEquivalent = "15 000 " + @@ -230,7 +228,7 @@ private val token = TokenToSelectState.TokenToSelect( ) private val title = TokenToSelectState.Title( - title = "MY TOKENS", + title = stringReference("MY TOKENS"), ) @Preview @@ -243,7 +241,6 @@ private fun TokenScreenPreview() { unavailableTokens = listOf(title, token, token, token).toImmutableList(), onSearchEntered = {}, onTokenSelected = {}, - network = Network("Ethereum", "ETH"), ), onBack = {}, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt index 65abcf814c..c9018a2345 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt @@ -4,6 +4,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.feature.swap.domain.models.domain.Currency import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData import com.tangem.feature.swap.domain.models.ui.SwapStateData +import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress import com.tangem.feature.swap.domain.models.ui.TxFee data class SwapProcessDataState( @@ -20,4 +21,5 @@ data class SwapProcessDataState( val approveDataModel: RequestApproveStateData? = null, val swapDataModel: SwapStateData? = null, val selectedFee: TxFee? = null, + val tokensDataState: TokensDataStateExpress? = null, ) \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 99919c05a3..0f2d531f35 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -4,9 +4,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.* +import arrow.core.getOrElse import com.tangem.common.Provider import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.InputNumberFormatter +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.feature.swap.analytics.SwapEvents @@ -27,11 +30,9 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer import com.tangem.utils.coroutines.runCatching import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json import timber.log.Timber import java.text.DecimalFormat import java.text.NumberFormat @@ -47,6 +48,7 @@ internal class SwapViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { @@ -55,9 +57,12 @@ internal class SwapViewModel @Inject constructor( private var isBalanceHidden = true + private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() + private val stateBuilder = StateBuilder( actions = createUiActions(), isBalanceHiddenProvider = Provider { isBalanceHidden }, + appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), ) private val inputNumberFormatter = @@ -87,7 +92,7 @@ internal class SwapViewModel @Inject constructor( derivationPath = initialCryptoCurrency.network.derivationPath.value, network = initialCryptoCurrency.network, ) - initTokens(initialCryptoCurrency) + initTokens() } override fun onCreate(owner: LifecycleOwner) { @@ -126,7 +131,7 @@ internal class SwapViewModel @Inject constructor( } @Suppress("UnusedPrivateMember") - private fun initTokens(currency: CryptoCurrency) { + private fun initTokens() { // new flow viewModelScope.launch(dispatchers.main) { runCatching(dispatchers.io) { @@ -134,10 +139,11 @@ internal class SwapViewModel @Inject constructor( }.onSuccess { state -> dataState = dataState.copy( fromCryptoCurrency = initialCryptoCurrency, - toCryptoCurrency = state.toGroup.available.first().currencyStatus.currency + toCryptoCurrency = state.toGroup.available.first().currencyStatus.currency, + tokensDataState = state, ) - // updateTokensState(dataState = state.foundTokensState) + updateTokensState(state) startLoadingQuotes( fromToken = initialCryptoCurrency, toToken = state.toGroup.available.first().currencyStatus.currency, @@ -171,11 +177,11 @@ internal class SwapViewModel @Inject constructor( // } } - private fun updateTokensState(dataState: FoundTokensStateExpress) { + private fun updateTokensState(dataState: TokensDataStateExpress) { + val tokensDataState = if (!isOrderReversed) dataState.toGroup else dataState.fromGroup uiState = stateBuilder.addTokensToState( uiState = uiState, - dataState = dataState, - networkInfo = blockchainInteractor.getBlockchainInfo(initialCryptoCurrency.network.backendId), + tokensDataState = tokensDataState, ) } @@ -369,28 +375,31 @@ internal class SwapViewModel @Inject constructor( swapInteractor.searchTokens(dataState.networkId, searchQuery) } .onSuccess { - updateTokensState(it) + // updateTokensState(it) } .onFailure { } } } private fun onTokenSelect(id: String) { - val foundToken = swapInteractor.findTokenById(id) + val tokens = dataState.tokensDataState ?: return + val foundToken = tokens.toGroup.available.firstOrNull { + it.currencyStatus.currency.id.value == id + } analyticsEventHandler.send( - event = SwapEvents.SearchTokenClicked(currencySymbol = foundToken?.symbol), + event = SwapEvents.SearchTokenClicked(currencySymbol = foundToken?.currencyStatus?.currency?.symbol), ) if (foundToken != null) { val fromToken: CryptoCurrency val toToken: CryptoCurrency if (isOrderReversed) { - fromToken = foundToken + fromToken = foundToken.currencyStatus.currency toToken = initialCryptoCurrency } else { fromToken = initialCryptoCurrency - toToken = foundToken + toToken = foundToken.currencyStatus.currency } dataState = dataState.copy( fromCryptoCurrency = fromToken, @@ -532,6 +541,18 @@ internal class SwapViewModel @Inject constructor( ) } + private fun createSelectedAppCurrencyFlow(): StateFlow { + return getSelectedAppCurrencyUseCase() + .map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) + } + companion object { private const val loggingTag = "SwapViewModel" private const val INITIAL_AMOUNT = "" From 60d8a4f2a320313fd773e906f18093c96ab9c97d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 Nov 2023 12:03:02 +0300 Subject: [PATCH 6/6] Updated on 2026-08-14 --- .../local/quote/DefaultQuotesStore.kt | 21 +++++---- ...> GetCryptoCurrencyStatusesSyncUseCase.kt} | 11 ++--- .../CurrenciesStatusesOperations.kt | 38 ++++++--------- .../feature/swap/domain/SwapInteractorImpl.kt | 6 +-- .../swap/domain/di/SwapDomainModule.kt | 8 ++-- .../swap/converters/TokensDataConverter.kt | 46 +++++++++++++++---- .../feature/swap/ui/SwapSelectTokenScreen.kt | 21 +++++---- 7 files changed, 83 insertions(+), 68 deletions(-) rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/{GetCryptoCurrencyStatusesUseCase.kt => GetCryptoCurrencyStatusesSyncUseCase.kt} (73%) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt index e28a90bfb2..fc0d1e0e42 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt @@ -4,8 +4,8 @@ import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.quote.model.StoredQuote import com.tangem.domain.tokens.model.CryptoCurrency -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow +import com.tangem.utils.extensions.addOrReplace +import kotlinx.coroutines.flow.* internal class DefaultQuotesStore( private val dataStore: StringKeyDataStore, @@ -13,17 +13,20 @@ internal class DefaultQuotesStore( override fun get(currenciesIds: Set): Flow> { return channelFlow { - if (dataStore.isEmpty()) { + val flows = currenciesIds.mapNotNull { currencyId -> + currencyId.rawCurrencyId?.let(dataStore::get) + } + + if (dataStore.isEmpty() || flows.isEmpty()) { send(emptySet()) } - val quotes = currenciesIds.mapNotNull { currencyId -> - currencyId.rawCurrencyId?.let { - dataStore.getSyncOrNull(it) + merge(*flows.toTypedArray()) + .scan>(emptySet()) { acc, quote -> + acc.addOrReplace(quote) { it.rawCurrencyId == quote.rawCurrencyId } } - } - - send(quotes.toSet()) + .filter(Set::isNotEmpty) + .collect(::send) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesSyncUseCase.kt similarity index 73% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesSyncUseCase.kt index 2734f1b2e3..cdf58b3080 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusesSyncUseCase.kt @@ -10,16 +10,15 @@ import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.* -class GetCryptoCurrencyStatusesUseCase( +class GetCryptoCurrencyStatusesSyncUseCase( internal val currenciesRepository: CurrenciesRepository, internal val quotesRepository: QuotesRepository, internal val networksRepository: NetworksRepository, internal val dispatchers: CoroutineDispatcherProvider, ) { - operator fun invoke(userWalletId: UserWalletId): Flow>> { + suspend operator fun invoke(userWalletId: UserWalletId): Either> { val operations = CurrenciesStatusesOperations( userWalletId = userWalletId, currenciesRepository = currenciesRepository, @@ -27,9 +26,7 @@ class GetCryptoCurrencyStatusesUseCase( networksRepository = networksRepository, ) - return operations.getCurrenciesStatusesMergedFlow() - .map { maybeCurrenciesStatuses -> - maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError) - } + return operations.getCurrenciesStatusesSync() + .mapLeft { error -> error.mapToTokenListError() } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 60e3d3b4b0..3ce6112296 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -67,33 +67,21 @@ internal class CurrenciesStatusesOperations( } } - fun getCurrenciesStatusesMergedFlow(): Flow>> { - return getMultiCurrencyWalletCurrencies().transformLatest { maybeCurrencies -> - val nonEmptyCurrencies = maybeCurrencies.fold( - ifLeft = { error -> - emit(error.left()) - return@transformLatest + suspend fun getCurrenciesStatusesSync(): Either> { + return either { + catch( + block = { + val nonEmptyCurrencies = + currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId).toNonEmptyListOrNull() + ?: return emptyList().right() + val (networks, currenciesIds) = getIds(nonEmptyCurrencies) + val quotes = quotesRepository.getQuotesSync(currenciesIds, false).right() + val networkStatuses = + networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right() + return createCurrenciesStatuses(nonEmptyCurrencies, quotes, networkStatuses) }, - ifRight = List::toNonEmptyListOrNull, + catch = { raise(Error.DataError(it)) }, ) - - if (nonEmptyCurrencies == null) { - val emptyCurrenciesStatuses = emptyList() - - emit(emptyCurrenciesStatuses.right()) - return@transformLatest - } - - val (networks, currenciesIds) = getIds(nonEmptyCurrencies) - - val currenciesFlow = combine( - getQuotes(currenciesIds), - getNetworksStatuses(networks), - ) { maybeQuotes, maybeNetworksStatuses -> - createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses) - } - - emitAll(currenciesFlow) } } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index f9d1faa5bb..ec089f6df7 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain import arrow.core.getOrElse import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.GetCryptoCurrencyStatusesUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Network @@ -22,7 +22,6 @@ import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import com.tangem.utils.toFiatString -import kotlinx.coroutines.flow.first import timber.log.Timber import java.math.BigDecimal import java.math.RoundingMode @@ -39,7 +38,7 @@ internal class SwapInteractorImpl @Inject constructor( private val networksRepository: NetworksRepository, private val walletFeatureToggles: WalletFeatureToggles, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesUseCase, + private val getMultiCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase, ) : SwapInteractor { // TODO: Move to DI @@ -61,7 +60,6 @@ internal class SwapInteractorImpl @Inject constructor( requireNotNull(selectedWallet) { "No selected wallet" } val walletCurrencyStatuses = getMultiCryptoCurrencyStatusUseCase(selectedWallet.walletId) - .first() .getOrElse { emptyList() } val walletCurrencyStatusesExceptInitial = walletCurrencyStatuses.filter { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt index 3169f16427..7fa93f1c70 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/di/SwapDomainModule.kt @@ -2,7 +2,7 @@ package com.tangem.feature.swap.domain.di import com.tangem.domain.tokens.GetCardTokensListUseCase import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase -import com.tangem.domain.tokens.GetCryptoCurrencyStatusesUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository @@ -35,7 +35,7 @@ class SwapDomainModule { networksRepository: NetworksRepository, walletFeatureToggles: WalletFeatureToggles, @SwapScope getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - @SwapScope getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesUseCase, + @SwapScope getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusesSyncUseCase, ): SwapInteractor { return SwapInteractorImpl( transactionManager = transactionManager, @@ -81,8 +81,8 @@ class SwapDomainModule { quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, - ): GetCryptoCurrencyStatusesUseCase { - return GetCryptoCurrencyStatusesUseCase( + ): GetCryptoCurrencyStatusesSyncUseCase { + return GetCryptoCurrencyStatusesSyncUseCase( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt index 03047e24b8..e3eff35d4c 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/TokensDataConverter.kt @@ -2,10 +2,13 @@ package com.tangem.feature.swap.converters import com.tangem.common.Provider import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.extensions.getTintForTokenIcon import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup @@ -26,13 +29,13 @@ class TokensDataConverter( val availableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource val unavailableTitle = TokenToSelectState.Title(stringReference("My tokens")) // todo replace with resource return SwapSelectTokenStateHolder( - availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it) } + availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it, true) } .toMutableList() .apply { this.add(0, availableTitle) } .toImmutableList(), - unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it) } + unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) } .toMutableList() .apply { this.add(0, unavailableTitle) @@ -43,18 +46,17 @@ class TokensDataConverter( ) } - private fun tokenWithBalanceToTokenToSelect(cryptoCurrencySwapInfo: CryptoCurrencySwapInfo): TokenToSelectState { + private fun tokenWithBalanceToTokenToSelect( + cryptoCurrencySwapInfo: CryptoCurrencySwapInfo, + isAvailable: Boolean, + ): TokenToSelectState { val cryptoCurrencyStatus = cryptoCurrencySwapInfo.currencyStatus return TokenToSelectState.TokenToSelect( id = cryptoCurrencyStatus.currency.id.value, name = cryptoCurrencyStatus.currency.name, symbol = cryptoCurrencyStatus.currency.symbol, - tokenIcon = TokenIconState.CoinIcon( - url = cryptoCurrencyStatus.currency.iconUrl, - fallbackResId = cryptoCurrencyStatus.currency.networkIconResId, - isGrayscale = false, - showCustomBadge = cryptoCurrencyStatus.currency.isCustom, - ), + available = isAvailable, + tokenIcon = convertIcon(cryptoCurrencyStatus.currency, isAvailable), addedTokenBalanceData = TokenBalanceData( amount = formatCryptoAmount(cryptoCurrencyStatus), amountEquivalent = formatFiatAmount(cryptoCurrencyStatus, appCurrencyProvider.invoke()), @@ -63,6 +65,32 @@ class TokensDataConverter( ) } + private fun convertIcon(currency: CryptoCurrency, isAvailable: Boolean): TokenIconState { + return when (currency) { + is CryptoCurrency.Coin -> { + TokenIconState.CoinIcon( + url = currency.iconUrl, + fallbackResId = currency.networkIconResId, + isGrayscale = !isAvailable, + showCustomBadge = currency.isCustom, + ) + } + is CryptoCurrency.Token -> { + val isGrayscale = currency.network.isTestnet + val background = currency.tryGetBackgroundForTokenIcon(isGrayscale) + val tint = getTintForTokenIcon(background) + TokenIconState.TokenIcon( + url = currency.iconUrl, + isGrayscale = !isAvailable, + showCustomBadge = currency.isCustom, + networkBadgeIconResId = currency.networkIconResId, + fallbackTint = tint, + fallbackBackground = background, + ) + } + } + } + private fun formatCryptoAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): String { return BigDecimalFormatter.formatCryptoAmount( cryptoCurrencyStatus.value.amount, 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 2bbeedc808..6e89f78c3a 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 @@ -111,7 +111,7 @@ private fun TitleHeader(item: TokenToSelectState.Title, modifier: Modifier = Mod .background(TangemTheme.colors.background.action), ) { Text( - text = item.title.resolveReference(), + text = item.title.resolveReference().uppercase(), style = TangemTheme.typography.overline, modifier = Modifier .padding( @@ -133,7 +133,10 @@ private fun TokenItem( modifier = modifier .fillMaxWidth() .height(TangemTheme.dimens.size72) - .clickable(onClick = onTokenClick) + .clickable( + enabled = token.available, + onClick = onTokenClick, + ) .padding( vertical = TangemTheme.dimens.spacing14, horizontal = TangemTheme.dimens.spacing16, @@ -169,13 +172,7 @@ private fun TokenItem( Spacer(modifier = Modifier.weight(1f)) - if (!token.available) { - Text( - text = stringResource(id = R.string.swapping_token_not_available), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - ) - } else if (token.addedTokenBalanceData != null) { + if (token.addedTokenBalanceData != null) { Column( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Center, @@ -190,7 +187,11 @@ private fun TokenItem( token.addedTokenBalanceData.amountEquivalent.orEmpty() }, style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.primary1, + color = if (token.available) { + TangemTheme.colors.text.primary1 + } else { + TangemTheme.colors.text.tertiary + }, ) SpacerW2() Text(